Стрелка на кривой Безье

176
02 января 2019, 16:10

Подскажите, как можно нарисовать в конце кривой Безье стрелку? Есть множество примеров как это сделать для прямой линии (1 , 2 , 3), но именно для кривой у меня не получается это сделать.

Есть такой код, но отрабатывает он как на гифке.

xaml:

<Canvas>
  <Path x:Name="pathMain" Stroke="Blue" StrokeThickness="2"/>
</Canvas>

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);, то отрабатывает так:

Answer 1

Я дорисовал опорные точки к вашей кривой (красная ломаная на анимации):

Дело в том, что стрелка показывает правильно: направление последнего сегмента опорной линии. Стрелка и правда показывает направление касательной в крайней точке. Но ваше вычисление промежуточных точек делает слишком большой излом, поэтому видимое направление кривой слишком отличается от направления касательной.

Попробуйте поменять алгоритм выбора промежуточных точек, проблема, думаю, в нём. (Не могу предложить свой алгоритм, потому что не знаю, какая ваша задача.)

По поводу алгоритма, вот такой код ведёт себя намного лучше:

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:

<Canvas Background="Transparent"
        MouseLeftButtonDown="OnMouseLeftButtonDown"
        MouseLeftButtonUp="OnMouseLeftButtonUp"
        MouseMove="OnMouseMove">
    <Path x:Name="pathMain" Stroke="Blue" StrokeThickness="2"/>
</Canvas>

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);
    }
}
READ ALSO
С# поиск bytes в большом файле и замена

С# поиск bytes в большом файле и замена

Надо поменять в 4 местах hex значение в большом файле 6 gb Использую этот код

174
Как создать анимацию в Unity

Как создать анимацию в Unity

Подскажите, как запустить анимацию пушки во время выстрела ядра из нее? Создание и выстрел ядра у меня происходит в методе Shoot()Не понимаю...

216
Цикл по переменным

Цикл по переменным

Что есть:

177
Парсинг типа данных PointF

Парсинг типа данных PointF

Добрый день!Мне необходимо пропарсить PointFМетод Parse не подходит

179