Подскажите, как можно нарисовать в конце кривой Безье стрелку?
Есть множество примеров как это сделать для прямой линии (1 , 2 , 3), но именно для кривой у меня не получается это сделать.
Есть такой код, но отрабатывает он как на гифке.
xaml:
code-behind:
public PolyLineSegment DrawArrow(Point a, Point b)
{
double HeadWidth = 10; // Ширина между ребрами стрелки
double HeadHeight = 5; // Длина ребер стрелки
double X1 = a.X;
double Y1 = a.Y;
double X2 = b.X;
double Y2 = b.Y;
double theta = Math.Atan2(Y1 - Y2, X1 - X2);
double sint = Math.Sin(theta);
double cost = Math.Cos(theta);
Point pt3 = new Point(
X2 + (HeadWidth * cost - HeadHeight * sint),
Y2 + (HeadWidth * sint + HeadHeight * cost));
Point pt4 = new Point(
X2 + (HeadWidth * cost + HeadHeight * sint),
Y2 - (HeadHeight * cost - HeadWidth * sint));
PolyLineSegment arrow = new PolyLineSegment();
arrow.Points.Add(b);
arrow.Points.Add(pt3);
arrow.Points.Add(pt4);
arrow.Points.Add(b);
return arrow;
}
private void DrawLine(MouseEventArgs e)
{
Point endPoint = e.GetPosition(this);
PathFigure pathFigure = new PathFigure
{
StartPoint = new Point(800, 500),
IsClosed = false
};
//Кривая Безье
Vector vector = endPoint - pathFigure.StartPoint;
Point point1 = new Point(pathFigure.StartPoint.X + vector.X / 2, pathFigure.StartPoint.Y);
Point point2 = new Point(pathFigure.StartPoint.X + vector.X / 1.5, pathFigure.StartPoint.Y + vector.Y/ 0.95);
BezierSegment curve = new BezierSegment(point1, point2, endPoint, true);
PolyLineSegment arrow = DrawArrow(pathFigure.StartPoint, endPoint);
PathGeometry path = new PathGeometry();
path.Figures.Add(pathFigure);
pathFigure.Segments.Add(curve);
pathFigure.Segments.Add(arrow);
pathMain.Data = path;
}
Если DrawArrow(point2, endPoint) вместо DrawArrow(pathFigure.StartPoint, endPoint);, то отрабатывает так:
Ответ
Я дорисовал опорные точки к вашей кривой (красная ломаная на анимации):
Дело в том, что стрелка показывает правильно: направление последнего сегмента опорной линии. Стрелка и правда показывает направление касательной в крайней точке. Но ваше вычисление промежуточных точек делает слишком большой излом, поэтому видимое направление кривой слишком отличается от направления касательной.
Попробуйте поменять алгоритм выбора промежуточных точек, проблема, думаю, в нём. (Не могу предложить свой алгоритм, потому что не знаю, какая ваша задача.)
По поводу алгоритма, вот такой код ведёт себя намного лучше:
var startPoint = pathFigure.StartPoint;
//Кривая Безье
Vector vector = endPoint - startPoint;
Point point1 = new Point(startPoint.X + 3 * vector.X / 8, startPoint.Y + 1 * vector.Y / 8);
Point point2 = new Point(startPoint.X + 5 * vector.X / 8, startPoint.Y + 7 * vector.Y / 8);
Вот на всякий случай полный пример:
MainWindow.xaml:
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public PolyLineSegment DrawArrow(Point a, Point b)
{
double HeadWidth = 10; // Ширина между ребрами стрелки
double HeadHeight = 5; // Длина ребер стрелки
double X1 = a.X;
double Y1 = a.Y;
double X2 = b.X;
double Y2 = b.Y;
double theta = Math.Atan2(Y1 - Y2, X1 - X2);
double sint = Math.Sin(theta);
double cost = Math.Cos(theta);
Point pt3 = new Point(
X2 + (HeadWidth * cost - HeadHeight * sint),
Y2 + (HeadWidth * sint + HeadHeight * cost));
Point pt4 = new Point(
X2 + (HeadWidth * cost + HeadHeight * sint),
Y2 - (HeadHeight * cost - HeadWidth * sint));
PolyLineSegment arrow = new PolyLineSegment();
arrow.Points.Add(b);
arrow.Points.Add(pt3);
arrow.Points.Add(pt4);
arrow.Points.Add(b);
return arrow;
}
private void DrawLine(MouseEventArgs e, UIElement host)
{
PathFigure pathFigure = ((PathGeometry)pathMain.Data).Figures[0];
Point startPoint = pathFigure.StartPoint;
Point endPoint = e.GetPosition(host);
//Кривая Безье
Vector vector = endPoint - startPoint;
Point point1 = new Point(startPoint.X + 3 * vector.X / 8,
startPoint.Y + 1 * vector.Y / 8);
Point point2 = new Point(startPoint.X + 5 * vector.X / 8,
startPoint.Y + 7 * vector.Y / 8);
BezierSegment curve = new BezierSegment(point1, point2, endPoint, true);
PolyLineSegment arrow = DrawArrow(point2, endPoint);
pathFigure.Segments.Clear();
pathFigure.Segments.Add(curve);
pathFigure.Segments.Add(arrow);
}
private UIElement currentHost;
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var host = (UIElement)sender;
currentHost = host;
Mouse.Capture(host);
Point startPoint = e.GetPosition(host);
PathFigure pathFigure = new PathFigure
{
StartPoint = startPoint,
IsClosed = false
};
pathMain.Data = new PathGeometry() { Figures = { pathFigure } };
DrawLine(e, currentHost);
}
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Mouse.Capture(null);
DrawLine(e, currentHost);
currentHost = null;
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (currentHost != null)
DrawLine(e, currentHost);
}
}
Комментариев нет:
Отправить комментарий