Страницы

Поиск по вопросам

вторник, 26 ноября 2019 г.

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


Подскажите, как можно нарисовать в конце кривой Безье стрелку?
Есть множество примеров как это сделать для прямой линии (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);, то отрабатывает так:

    


Ответы

Ответ 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: 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); } }

Комментариев нет:

Отправить комментарий