项目中要用到样条曲线,必须过顶点,圆滑后还不能太走样,捣鼓一番,发现里面颇有玄机,于是把我多方抄来改造的方法发出来,方便新手:
如上图,看代码吧:
前台页面:
XML
<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>
后台代码:
cs
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;
}
}
}