WPF 使用贝塞尔绘制拖拽曲线

using HslCommunication;
using Microsoft.Windows.Controls.Ribbon;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace _106_CreatePLC
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void CreatePLC_Click(object sender, RoutedEventArgs e)
        {

        }
        private ObservableCollection<Point> itemsSource = new ObservableCollection<Point>();
        public ObservableCollection<Point> ItemsSource { get => itemsSource; set => itemsSource = value; }
        private void ItemsSource_CollectionChanged()
        {
            var originalPt = new Ellipse { Width = 8, Height = 8, Margin = new Thickness(0, 0, 0, 0), Fill = Brushes.Yellow };
            originalPt.HorizontalAlignment = HorizontalAlignment.Left;
            originalPt.VerticalAlignment = VerticalAlignment.Top;
            Can2.Children.Add(originalPt);
            DrawPath(ItemsSource);
        }

        private void DrawPath(ObservableCollection<Point> itemsSource)
        {
            //var orderSource = from pt in itemsSource orderby pt.X ascending select pt;
            //var li = orderSource.ToList();
            var li = itemsSource.ToList();

            var pf = new PathFigure { StartPoint = li[0] };
            for (var i = 0; i < li.Count - 1; i++)
            {
                int current = i, last = i - 1, next = i + 1, next2 = i + 2;
                if (last == -1)
                {
                    last = 0;
                }
                if (next == li.Count)
                {
                    next = li.Count - 1;
                }
                if (next2 == li.Count)
                {
                    next2 = li.Count - 1;
                }
                var bzs = GetBezierSegment(li[current], li[last], li[next], li[next2]);
                pf.Segments.Add(bzs);
            }
            //画点的圆圈
            li.ForEach(lipt => Can2.Children.Add(new Ellipse { Width = 4, Height = 4, Margin = new Thickness(lipt.X - 2, lipt.Y - 2, 0, 0), HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Top, Fill = Brushes.Red, ToolTip = string.Format("x:{0},y:{1} ", lipt.X, lipt.Y) }));


            //添加曲线到图上
            var pfc = new PathFigureCollection { pf };
            var pg = new PathGeometry(pfc);
            var path = new Path { StrokeThickness = 1, Stroke = Brushes.Green, Data = pg };
            Can2.Children.Add(path);
        }
        /// <summary>
        /// 获得贝塞尔曲线
        /// </summary>
        /// <param name="currentPt">当前点</param>
        /// <param name="lastPt">上一个点</param>
        /// <param name="nextPt1">下一个点1</param>
        /// <param name="nextPt2">下一个点2</param>
        /// <returns></returns>
        private BezierSegment GetBezierSegment(Point currentPt, Point lastPt, Point nextPt1, Point nextPt2)
        {
            //计算中点
            var lastC = GetCenterPoint(lastPt, currentPt);
            var nextC1 = GetCenterPoint(currentPt, nextPt1); //贝塞尔控制点
            var nextC2 = GetCenterPoint(nextPt1, nextPt2);

            //计算相邻中点连线跟目的点的垂足
            //效果并不算太好,因为可能点在两个线上或者线的延长线上,计算会有误差
            //所以就直接使用中点平移方法。
            //var C1 = GetFootPoint(lastC, nextC1, currentPt);
            //var C2 = GetFootPoint(nextC1, nextC2, nextPt1);


            //计算“相邻中点”的中点
            var c1 = GetCenterPoint(lastC, nextC1);
            var c2 = GetCenterPoint(nextC1, nextC2);


            //计算【"中点"的中点】需要的点位移
            var controlPtOffset1 = currentPt - c1;
            var controlPtOffset2 = nextPt1 - c2;

            //移动控制点
            var controlPt1 = nextC1 + controlPtOffset1;
            var controlPt2 = nextC1 + controlPtOffset2;

            //如果觉得曲线幅度太大,可以将控制点向当前点靠近一定的系数。
            controlPt1 = controlPt1 + 0 * (currentPt - controlPt1);
            controlPt2 = controlPt2 + 0 * (nextPt1 - controlPt2);

            var bzs = new BezierSegment(controlPt1, controlPt2, nextPt1, true);
            return bzs;
        }
        private Point GetCenterPoint(Point pt1, Point pt2)
        {
            return new Point((pt1.X + pt2.X) / 2, (pt1.Y + pt2.Y) / 2);
        }
        Point arcsP = new Point();
        Point arceP = new Point();
        Point arcM42P = new Point();
        Point arcM43P = new Point();
        private void Can2_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            sel2 = 0;
            if (sel2 == 0)
            {
                Can2.Children.Clear();
                arcsP = e.GetPosition((Canvas)sender);
                sel2 = 1;
            }
        }
        int sel2 = 0;
        private void Can2_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (sel2 == 1 && e.LeftButton == MouseButtonState.Pressed)
            {
                Can2.Children.Clear();
                ItemsSource.Clear();
                arceP = e.GetPosition((Canvas)sender);

                arcM42P.X = arcsP.X + (arceP.X - arcsP.X) / 4 * 2+25;
                arcM42P.Y = arcsP.Y + (arceP.Y - arcsP.Y) / 4 * 2+35;
                arcM43P.X = arcsP.X + (arceP.X - arcsP.X) / 4 * 3;
                arcM43P.Y = arcsP.Y + (arceP.Y - arcsP.Y) / 4 * 3+10;

                ItemsSource.Add(arcsP);
                ItemsSource.Add(arcM42P);
                ItemsSource.Add(arcM43P);
                ItemsSource.Add(arceP);
                ItemsSource_CollectionChanged();
            }
            if (sel2 == 1 && e.LeftButton == MouseButtonState.Released)
            {
                Can2.Children.Clear();
                ItemsSource.Clear();
                arceP = e.GetPosition((Canvas)sender);

                arcM42P.X = arcsP.X + (arceP.X - arcsP.X) / 4 * 2 + 25;
                arcM42P.Y = arcsP.Y + (arceP.Y - arcsP.Y) / 4 * 2 + 35;
                arcM43P.X = arcsP.X + (arceP.X - arcsP.X) / 4 * 3 ;
                arcM43P.Y = arcsP.Y + (arceP.Y - arcsP.Y) / 4 * 3 + 10;

                ItemsSource.Add(arcsP);
                ItemsSource.Add(arcM42P);
                ItemsSource.Add(arcM43P);
                ItemsSource.Add(arceP);
                ItemsSource_CollectionChanged();

                sel2 = 2;
            }
        }
        Point sP = new Point();
        Point eP = new Point();

        int sel = 0;
        private void Can1_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            sel = 0;
            if (sel == 0)
            {
                sP = e.GetPosition((Canvas)sender);
                sel = 1;
            }
        }
        private void Can1_PreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            sel = 2;
            if (true)
            {
                if (Can1.Children.Contains(l2) == true) Can1.Children.Remove(l2);
                eP = e.GetPosition((Canvas)sender);
                Line l1 = new Line();
                l1.Stroke = Brushes.Black;
                l1.StrokeThickness = 2;
                l1.X1 = sP.X;
                l1.Y1 = sP.Y;
                l1.X2 = eP.X;
                l1.Y2 = eP.Y;
                Can1.Children.Add(l1);
                sel = 3;
            }
        }
        Line l2 = new Line();

        private void Can1_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (sel == 1)
            {
                if (Can1.Children.Contains(l2) == true) Can1.Children.Remove(l2);

                eP = e.GetPosition((Canvas)sender);
                l2.Stroke = Brushes.Black;
                l2.StrokeThickness = 2;
                l2.X1 = sP.X;
                l2.Y1 = sP.Y;
                l2.X2 = eP.X;
                l2.Y2 = eP.Y;
                Can1.Children.Add(l2);
            }
        }
        private void DrawPath()
        {
            Path path = new Path();
            //300,300起始坐标
            //A Arc圆弧
            //100,100圆弧的X,Y值
            //0 (图形旋转角度)   1(当设置旋转角度时,1取大圆弧0取小圆弧)  1(1为顺时针0为逆时针)
            //300,0 表示终止坐标
            path.Data = Geometry.Parse("M 300,300 A 100,100 0 1 1 300,0");
            path.Stroke = Brushes.Black;
            path.StrokeThickness = 2;
            mainGrid.Children.Add(path);
        }
    }
}
 

xaml代码:

<Window x:Class="_106_CreatePLC.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:_106_CreatePLC"
        mc:Ignorable="d"
        Title="MainWindow" Height="1000" Width="1800">
    <Grid Name="mainGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="300"></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition ></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="30"></RowDefinition>
                <RowDefinition Height="30"></RowDefinition>
                <RowDefinition></RowDefinition>
            </Grid.RowDefinitions>
            <Button Name="CreatePLC" Content="创建PLC" Click="CreatePLC_Click" Grid.Row="0" Grid.Column="0"></Button>
        </Grid>
        <Canvas Name="Can1" Grid.Row="0" Grid.Column="2" 
                Background="WhiteSmoke" Grid.RowSpan="2" 
                Grid.ColumnSpan="1" PreviewMouseDown="Can1_PreviewMouseDown"
                PreviewMouseUp="Can1_PreviewMouseUp"
                PreviewMouseMove="Can1_PreviewMouseMove"
                >
            <local:ReadImage x:Name="readImageFB1"
                Margin="156,99,-156,-99"
                             ></local:ReadImage>

            <local:ReadImage x:Name="readImageFB2"
                Margin="161,316,-161,-316"
                             ></local:ReadImage>
        </Canvas>
        <Canvas Name="Can2" Grid.Row="0" Grid.Column="1" 
                Background="WhiteSmoke" Grid.RowSpan="2" 
                Grid.ColumnSpan="1" PreviewMouseDown="Can2_PreviewMouseDown"
                PreviewMouseMove="Can2_PreviewMouseMove"
                Margin="0,0,5,0"
                >
        </Canvas>
    </Grid>
</Window>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值