贝塞尔曲线是由法国数学家Pierre Bézier所发明,由此为计算机矢量图形学奠定了基础。它的主要意义在于无论是直线或曲线都能在数学上予以描述。贝塞尔曲线就是这样的一条曲线,它是依据四个位置任意的点坐标绘制出的一条光滑曲线。
线性公式
给定点P0、P1,线性贝兹曲线只是一条两点之间的直线。这条线由下式给出:
![](https://i-blog.csdnimg.cn/blog_migrate/5b5c2de65ef897445ea7ae83ddbe3c8e.png)
且其等同于线性插值。
二次方公式
二次方贝兹曲线的路径由给定点P0、P1、P2的函数B(t)追踪:
![](https://i-blog.csdnimg.cn/blog_migrate/ad84e92b84a4867eb5b1a43914bae0ee.png)
TrueType字型就运用了以贝兹样条组成的二次贝兹曲线。
三次方公式
P0、P1、P2、P3四个点在平面或在三维空间中定义了三次方贝兹曲线。曲线起始于P0走向P1,并从P2的方向来到P3。一般不会经过P1或P2;这两个点只是在那里提供方向资讯。P0和P1之间的间距,决定了曲线在转而趋进P3之前,走向P2方向的“长度有多长”。
曲线的参数形式为:
![](https://i-blog.csdnimg.cn/blog_migrate/484a7b8b0ea548f152bb2ac668447771.png)
现代的成象系统,如PostScript、Asymptote和Metafont,运用了以贝兹样条组成的三次贝兹曲线,用来描绘曲线轮廓。
一般参数公式
阶贝兹曲线可如下推断。给定点P0、P1、…、Pn,其贝兹曲线即:
![](https://i-blog.csdnimg.cn/blog_migrate/02fdb03f679383541421c08d3006c786.png)
如上公式可如下递归表达: 用表示由点
P0、
P1、…、
Pn所决定的贝兹曲线。
用平常话来说,阶的贝兹曲线,即双阶贝兹曲线之间的插值。
公式说明
1.开始于P0并结束于Pn的曲线,即所谓的端点插值法属性。
2.曲线是直线的充分必要条件是所有的控制点都位在曲线上。同样的,贝塞尔曲线是直线的充分必要条件是控制点共线。
3.曲线的起始点(结束点)相切于贝塞尔多边形的第一节(最后一节)。
4.一条曲线可在任意点切割成两条或任意多条子曲线,每一条子曲线仍是贝塞尔曲线。
5.一些看似简单的曲线(如圆)无法以贝塞尔曲线精确的描述,或分段成贝塞尔曲线(虽然当每个内部控制点对单位圆上的外部控制点水平或垂直的的距离为时,分成四段的贝兹曲线,可以小于千分之一的最大半径误差近似于圆)。
6.位于固定偏移量的曲线(来自给定的贝塞尔曲线),又称作偏移曲线(假平行于原来的曲线,如两条铁轨之间的偏移)无法以贝兹曲线精确的形成(某些琐屑实例除外)。无论如何,现存的启发法通常可为实际用途中给出近似值。
上述是百度百科中关于贝塞尔曲线的详细描述,对于它的应用,最直观的,就像PS里的钢笔工具,今天,我将给出它的代码实现:
1,贝塞尔类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
namespace TestDemo
{
unsafe class Bezier
{
Point PointCubicBezier(Point[] cp, float t)
{
float ax, bx, cx, ay, by, cy, tS, tC;
cx = 1.0f * (cp[1].X - cp[0].X);
bx = 3.0f * (cp[2].X - cp[1].X) - cx;
ax = cp[3].X - cp[0].X - cx - bx;
cy = 1.0f * (cp[1].Y - cp[0].Y);
by = 3.0f * (cp[2].Y - cp[1].Y) - cy;
ay = cp[3].X - cp[0].Y - cx - by;
tS = t * t;
tC = tS * t;
int x = (int)((ax * tC) + (bx * tS) + (cx * t) + cp[0].X);
int y = (int)((ay * tC) + (by * tS) + (cy * t) + cp[0].Y);
return new Point(x, y);
}
public Bitmap DrawBezier(Bitmap src, Point[] cp)
{
Bitmap a = new Bitmap(src);
int w = a.Width;
int h = a.Height;
BitmapData srcData = a.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
byte* p = (byte*)srcData.Scan0;
float k = 0;
k = 1.0f / (w - 1);
Point temp;
for (int i = 0; i < w; i++)
{
temp = PointCubicBezier(cp, (float)i * k);
p[temp.X * 3 + temp.Y * srcData.Stride] = 0;
p[temp.X * 3 + 1 + temp.Y * srcData.Stride] = 0;
p[temp.X * 3 + 2 + temp.Y * srcData.Stride] = 0;
}
a.UnlockBits(srcData);
return a;
}
}
}
2,主界面代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
namespace TestDemo
{
unsafe public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
string startupPath = System.Windows.Forms.Application.StartupPath;
curBitmap = new Bitmap(startupPath + @"\mask.png");
//初始化
pa = new Point(button1.Location.X - pictureBox1.Location.X + 13, button1.Location.Y - pictureBox1.Location.Y + 11);
pb = new Point(button2.Location.X - pictureBox1.Location.X + 13, button2.Location.Y - pictureBox1.Location.Y + 11);
pc = new Point(button3.Location.X - pictureBox1.Location.X + 13, button3.Location.Y - pictureBox1.Location.Y + 11);
pd = new Point(button4.Location.X - pictureBox1.Location.X + 13, button4.Location.Y - pictureBox1.Location.Y + 11);
pictureBox1.Image = (Image)bezier.DrawBezier(curBitmap, new Point[] { pa, pb, pc, pd });
}
#region 变量声明
//当前图像变量
private Bitmap curBitmap = null;
private bool pointMoveStart = false;
private Point movePoint;
private Point pa;
private Point pb;
private Point pc;
private Point pd;
private Bezier bezier = new Bezier();
#endregion
#region Response
#endregion
#region MouseClick
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (pictureBox1.Image != null)
{
label1.Text = "X:" + e.X;
label2.Text = "Y:" + e.Y;
}
}
private void button1_MouseDown(object sender, MouseEventArgs e)
{
pointMoveStart = true;
if (e.Button == MouseButtons.Left)
movePoint = e.Location;
}
private void button1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && pointMoveStart)
{
button1.Location = new Point(button1.Location.X + e.X - movePoint.X, button1.Location.Y + e.Y - movePoint.Y);
}
}
private void button1_MouseUp(object sender, MouseEventArgs e)
{
pointMoveStart = false;
pa = new Point(button1.Location.X - pictureBox1.Location.X + 13, button1.Location.Y - pictureBox1.Location.Y + 11);
pb = new Point(button2.Location.X - pictureBox1.Location.X + 13, button2.Location.Y - pictureBox1.Location.Y + 11);
pc = new Point(button3.Location.X - pictureBox1.Location.X + 13, button3.Location.Y - pictureBox1.Location.Y + 11);
pd = new Point(button4.Location.X - pictureBox1.Location.X + 13, button4.Location.Y - pictureBox1.Location.Y + 11);
pictureBox1.Image = (Image)bezier.DrawBezier(curBitmap, new Point[] {pa, pb, pc, pd });
}
private void button2_MouseDown(object sender, MouseEventArgs e)
{
pointMoveStart = true;
if (e.Button == MouseButtons.Left)
movePoint = e.Location;
}
private void button2_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && pointMoveStart)
{
button2.Location = new Point(button2.Location.X + e.X - movePoint.X, button2.Location.Y + e.Y - movePoint.Y);
}
}
private void button2_MouseUp(object sender, MouseEventArgs e)
{
pointMoveStart = false;
pa = new Point(button1.Location.X - pictureBox1.Location.X + 13, button1.Location.Y - pictureBox1.Location.Y + 11);
pb = new Point(button2.Location.X - pictureBox1.Location.X + 13, button2.Location.Y - pictureBox1.Location.Y + 11);
pc = new Point(button3.Location.X - pictureBox1.Location.X + 13, button3.Location.Y - pictureBox1.Location.Y + 11);
pd = new Point(button4.Location.X - pictureBox1.Location.X + 13, button4.Location.Y - pictureBox1.Location.Y + 11);
pictureBox1.Image = (Image)bezier.DrawBezier(curBitmap, new Point[] { pa, pb, pc, pd });
}
private void button3_MouseDown(object sender, MouseEventArgs e)
{
pointMoveStart = true;
if (e.Button == MouseButtons.Left)
movePoint = e.Location;
}
private void button3_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && pointMoveStart)
{
button3.Location = new Point(button3.Location.X + e.X - movePoint.X, button3.Location.Y + e.Y - movePoint.Y);
}
}
private void button3_MouseUp(object sender, MouseEventArgs e)
{
pointMoveStart = false;
pa = new Point(button1.Location.X - pictureBox1.Location.X + 13, button1.Location.Y - pictureBox1.Location.Y + 11);
pb = new Point(button2.Location.X - pictureBox1.Location.X + 13, button2.Location.Y - pictureBox1.Location.Y + 11);
pc = new Point(button3.Location.X - pictureBox1.Location.X + 13, button3.Location.Y - pictureBox1.Location.Y + 11);
pd = new Point(button4.Location.X - pictureBox1.Location.X + 13, button4.Location.Y - pictureBox1.Location.Y + 11);
pictureBox1.Image = (Image)bezier.DrawBezier(curBitmap, new Point[] { pa, pb, pc, pd });
}
private void button4_MouseDown(object sender, MouseEventArgs e)
{
pointMoveStart = true;
if (e.Button == MouseButtons.Left)
movePoint = e.Location;
}
private void button4_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && pointMoveStart)
{
button4.Location = new Point(button4.Location.X + e.X - movePoint.X, button4.Location.Y + e.Y - movePoint.Y);
}
}
private void button4_MouseUp(object sender, MouseEventArgs e)
{
pointMoveStart = false;
pa = new Point(button1.Location.X - pictureBox1.Location.X + 13, button1.Location.Y - pictureBox1.Location.Y + 11);
pb = new Point(button2.Location.X - pictureBox1.Location.X + 13, button2.Location.Y - pictureBox1.Location.Y + 11);
pc = new Point(button3.Location.X - pictureBox1.Location.X + 13, button3.Location.Y - pictureBox1.Location.Y + 11);
pd = new Point(button4.Location.X - pictureBox1.Location.X + 13, button4.Location.Y - pictureBox1.Location.Y + 11);
pictureBox1.Image = (Image)bezier.DrawBezier(curBitmap, new Point[] { pa, pb, pc, pd });
}
#endregion
}
}
结果图如下:
最后给出完整C#代码DEMO:
贝塞尔曲线.zip