WPF 绘制过顶点的圆滑曲线(样条,贝塞尔)
项目中要用到样条曲线,必须过顶点,圆滑后还不能太走样,捣鼓一番,发现里面颇有玄机,于是把我多方抄来改造的方法发出来,方便新手:
如上图,看代码吧:
----------------------------------------
前台页面:
<Window x:Class="Wpf_north_demo.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:Wpf_north_demo"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800"><Grid><Canvas x:Name="ca1" Background="White" MouseLeftButtonDown="ca1_MouseLeftButtonDown" MouseMove="ca1_MouseMove" MouseRightButtonDown="ca1_MouseRightButtonDown"><Polyline x:Name="path_lines" Stroke="Silver" StrokeThickness="1" StrokeDashArray="1 1 1" IsHitTestVisible="False"></Polyline><Path x:Name="path1" Stroke="Red" StrokeThickness="1" IsHitTestVisible="False"><Path.Data><PathGeometry x:Name="pathGeometry1"></PathGeometry></Path.Data></Path></Canvas><Canvas x:Name="ca_top" IsHitTestVisible="False"/><TextBlock HorizontalAlignment="Left" VerticalAlignment="Top" Margin="5" Text="左键绘制,右键结束" IsHitTestVisible="False"/></Grid>
</Window>
后台代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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 Wpf_north_demo
{/// <summary>/// MainWindow.xaml 的交互逻辑/// </summary>public partial class MainWindow : Window{public MainWindow(){InitializeComponent();}int _num = 0;bool _started = false;List<Point> _seed = new List<Point>();private void ca1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e){if(!_started){_num = 0;_seed.Clear();_started = true;ca_top.Children.Clear();path_lines.Points.Clear();pathGeometry1.Figures.Clear();}while (path_lines.Points.Count > _num && _num > 0){path_lines.Points.RemoveAt(path_lines.Points.Count - 1);}_seed.Add(e.GetPosition(ca1));_num = _seed.Count;path_lines.Points.Add(_seed[_num - 1]);ca_top.Children.Add(new Ellipse{Width = 6,Height = 6,Stroke = Brushes.Blue,Fill = Brushes.Lime,Margin = new Thickness(_seed[_num - 1].X - 3, _seed[_num - 1].Y - 3, 0, 0)});}private void ca1_MouseMove(object sender, MouseEventArgs e){if (_started && e.LeftButton == MouseButtonState.Released && _num > 0){while (path_lines.Points.Count > _num){path_lines.Points.RemoveAt(path_lines.Points.Count - 1);}path_lines.Points.Add(e.GetPosition(ca1));}}private void ca1_MouseRightButtonDown(object sender, MouseButtonEventArgs e){if(_started){while (path_lines.Points.Count > _num && _num > 0){path_lines.Points.RemoveAt(path_lines.Points.Count - 1);}_seed.Add(e.GetPosition(ca1));_num = _seed.Count;path_lines.Points.Add(_seed[_num - 1]);ca_top.Children.Add(new Ellipse{Width = 6,Height = 6,Stroke = Brushes.Blue,Fill = Brushes.Lime,Margin = new Thickness(_seed[_num - 1].X - 3, _seed[_num - 1].Y - 3, 0, 0)});BezierHelper.DrawBezierPolyline(pathGeometry1, _seed, false);}else{_num = 0;_seed.Clear();ca_top.Children.Clear();path_lines.Points.Clear();pathGeometry1.Figures.Clear();}_started = false;}}public class BezierHelper{public static void DrawBezierPolyline(PathGeometry geo, List<Point> list, bool close){geo.Figures.Clear();if (list.Count > 0){PathFigure pf = new PathFigure() { IsClosed = close };pf.StartPoint = list[0];List<Point> controls = new List<Point>();for (int i = 0; i < list.Count; i++){Point control_01, control_02;GetControlPoint(list, i, out control_01, out control_02);controls.Add(control_01);controls.Add(control_02);}for (int i = 1; i < list.Count; i++){BezierSegment bs = new BezierSegment(controls[i * 2 - 1], controls[i * 2], list[i], true);bs.IsSmoothJoin = true;pf.Segments.Add(bs);}geo.Figures.Add(pf);}}static void GetControlPoint(List<Point> list, int idx, out Point control_01, out Point control_02){if (idx == 0){control_01 = list[0];}else{control_01 = GetAverage(list[idx - 1], list[idx]);}if (idx == list.Count - 1){control_02 = list[list.Count - 1];}else{control_02 = GetAverage(list[idx], list[idx + 1]);}Point ave = GetAverage(control_01, control_02);Point sh = Sub(list[idx], ave);control_01 = Mul(Add(control_01, sh), list[idx], 0.6);control_02 = Mul(Add(control_02, sh), list[idx], 0.6);}static Point GetAverage(Point x, Point y){return new Point((x.X + y.X) / 2, (x.Y + y.Y) / 2);}static Point Add(Point x, Point y){return new Point(x.X + y.X, x.Y + y.Y);}static Point Sub(Point x, Point y){return new Point(x.X - y.X, x.Y - y.Y);}static Point Mul(Point x, Point y, double d){Point temp = Sub(x, y);temp = new Point(temp.X * d, temp.Y * d);temp = Add(y, temp);return temp;}}}