1、一阶推导二阶,三阶类似推导
一阶情况下就是,一条直线。
P0(X1,Y1) P1(X2,Y2)
变量t 属于【0,1】
X2 = X1 + t *(X2-X1)
Y2 = Y1 + t *( Y2-Y1)
则一阶式子: B1(t)=P0+(P1−P0)t
当增加一个控制点 P2,则方程为 则线段有 P0P1 和 P1P2,一阶式代入,则方程就有:
B2(t)=(1−t)P0′+tP1′=(1−t)((1−t)P0+tP1)+t((1−t)P1+tP2)=(1−t)2P0+2t(1−t)P1+t2P2 ;
B2(t)=(1−t)2P0+2t(1−t)P1+t2P2,t∈[0,1]
三阶同样推理
/// <summary>
/// 贝赛尔曲线拟合算法
/// </summary>
public class BezierMathLib
{
#region 圆滑整条折线
/// <summary>
/// 圆滑闭合折线
/// </summary>
/// <param name="originPoints">需要圆滑点的数组</param>
/// <returns>Vector3数组</returns>
public static Vector3[] CloseSpline(Vector3[] originPoints)
{
return CloseSpline(originPoints, 0.1, 0.6);
}
/// <summary>
/// 圆滑闭合折线
/// </summary>
/// <param name="originPoints">需要圆滑点的数组</param>
/// <param name="t">圆润参数,在0-1之间,值越小越圆滑</param>
/// <returns>Vector3数组</returns>
public static Vector3[] CloseSpline(Vector3[] originPoints, double t)
{
return CloseSpline(originPoints, t, 0.6);
}
/// <summary>
/// 圆滑闭合折线
/// </summary>
/// <param name="originPoints">需要圆滑点的数组</param>
/// <param name="t">圆润参数,在0-1之间,值越小越圆滑</param>
/// <param name="scale">拉伸控制点值,在0-1之间</param>
/// <returns>Vector3数组</returns>
public static Vector3[] CloseSpline(Vector3[] originPoints, double t, double scale)
{//计算特征点
originPoints = CharacterPoints(originPoints);
Vector3[] originPoint = null;
if (originPoints[0].X == originPoints[originPoints.Length - 1].X && originPoints[0].Y == originPoints[originPoints.Length - 1].Y)
{
originPoint = new Vector3[originPoints.Length - 1];
for (int i = 0; i < originPoint.Length; i++)
originPoint[i] = originPoints[i];
}
else
{
originPoint = originPoints;
}
int originCount = originPoint.Length;
//控制点收缩系数 ,经调试0.6较好。
//double scale = 0.3;
Vector3[] midPoints = new Vector3[originCount];
for (int i = 0; i < originCount; i++)
{//生成中点
int nexti = (i + 1) % originCount;
midPoints[i] = new Vector3();
midPoints[i].X = (originPoint[i].X + originPoint[nexti].X) / 2.0f;
midPoints[i].Y = (originPoint[i].Y + originPoint[nexti].Y) / 2.0f;
}
//平移中点
Vector3[] extraPoints = new Vector3[2 * originCount];
Vector3 midinmid = new Vector3();
for (int i = 0; i < originCount; i++)
{
int nexti = (i + 1) % originCount;
int backi = (i + originCount - 1) % originCount;
midinmid.X = (midPoints[i].X + midPoints[backi].X) / 2.0f;
midinmid.Y = (midPoints[i].Y + midPoints[backi].Y) / 2.0f;
double offsetx = originPoint[i].X - midinmid.X;
double offsety = originPoint[i].Y - midinmid.Y;
int extraindex = 2 * i;
extraPoints[extraindex] = new Vector3();
extraPoints[extraindex].X = midPoints[backi].X + offsetx;
extraPoints[extraindex].Y = midPoints[backi].Y + offsety;
//朝 originPoint[i]方向收缩
double addx = (extraPoints[extraindex].X - originPoint[i].X) * scale;
double addy = (extraPoints[extraindex].Y - originPoint[i].Y) * scale;
extraPoints[extraindex].X = originPoint[i].X + addx;
extraPoints[extraindex].Y = originPoint[i].Y + addy;
int extranexti = (extraindex + 1) % (2 * originCount);
extraPoints[extranexti] = new Vector3();
extraPoints[extranexti].X = midPoints[i].X + offsetx;
extraPoints[extranexti].Y = midPoints[i].Y + offsety;
//朝 originPoint[i]方向收缩
addx = (extraPoints[extranexti].X - originPoint[i].X) * scale;
addy = (extraPoints[extranexti].Y - originPoint[i].Y) * scale;
extraPoints[extranexti].X = originPoint[i].X + addx;
extraPoints[extranexti].Y = originPoint[i].Y + addy;
}
List<Vector3> getPoints = new List<Vector3>();
Vector3[] controlPoint = new Vector3[4];
Vector3 tempP = null;
double px, py, pz;
for (int i = 0; i < originCount; i++)
{//生成4控制点,产生贝塞尔曲线
controlPoint[0] = originPoint[i];
int extraindex = 2 * i;
controlPoint[1] = extraPoints[extraindex + 1];
int extranexti = (extraindex + 2) % (2 * originCount);
controlPoint[2] = extraPoints[extranexti];
int nexti = (i + 1) % originCount;
controlPoint[3] = originPoint[nexti];
double u = 1;
while (u >= 0)
{
px = bezier3funcX(u, controlPoint);
py = bezier3funcY(u, controlPoint);
pz = bezier3funcZ(u, controlPoint);
tempP = new Vector3(px, py, pz);
getPoints.Add(tempP);//保存点
if (u == 0) break;
u -= t;
if (u < 0)
{
px = bezier3funcX(u, controlPoint);
py = bezier3funcY(u, controlPoint);
pz = bezier3funcZ(u, controlPoint);
tempP = new Vector3(px, py, pz);
getPoints.Add(tempP);//保存点
break;
}
}
}
Vector3[] pointVector3 = new Vector3[getPoints.Count];
getPoints.CopyTo(pointVector3, 0);
getPoints = null;
return pointVector3;
}
/// <summary>
/// 圆滑不闭合的折线
/// </summary>
/// <param name="originPoints">需要圆滑点的数组</param>
/// <returns>Vector3数组</returns>
public static Vector3[] OpenSpline(Vector3[] originPoints)
{
return OpenSpline(originPoints, 0.2, 4);
}
/// <summary>
/// 圆滑不闭合的折线
/// </summary>
/// <param name="originPoints">需要圆滑点的数组</param>
/// <param name="t">圆润参数,在0-1之间,值越小越圆滑</param>
/// <returns>Vector3数组</returns>
public static Vector3[] OpenSpline(Vector3[] originPoints, double t)
{
return OpenSpline(originPoints, t, 4);
}
/// <summary>
/// 圆滑不闭合的折线
/// </summary>
/// <param name="originPoints">需要圆滑点的数组</param>
/// <param name="t">圆润参数,在0-1之间,值越小越圆滑</param>
/// <param name="n">n,n值越大越平缓,n值小于需要圆滑点的个数</param>
/// <returns>Vector3数组</returns>
public static Vector3[] OpenSpline(Vector3[] originPoints, double t, int n)
{
int step = (int)(originPoints.Length / (n - 1));
Vector3[] controlP = new Vector3[n];
Vector3[] tempPoints = null;
List<Vector3> getPoints = new List<Vector3>();
bool sign = false;
for (int i = 0; i < step; i++)
{
for (int j = 0; j < n; j++)
{
int index = i * (n - 1) + j;
if (index > originPoints.Length - 1)
{
sign = true;
List<Vector3> pp = new List<Vector3>();
for (int k = 0; k < controlP.Length; k++)
{
if (controlP[k] != null)
pp.Add(controlP[k]);
}
Vector3[] tempPP = new Vector3[pp.Count];
pp.CopyTo(tempPP, 0);
tempPoints = CreateBezier(tempPP, t);
getPoints.AddRange(tempPoints);
break;
}
controlP[j] = originPoints[index];
}
if (sign == true)
break;
tempPoints = CreateBezier(controlP, t);
getPoints.AddRange(tempPoints);
}
tempPoints = new Vector3[getPoints.Count];
getPoints.CopyTo(tempPoints, 0);
return tempPoints;
}
/// <summary>
/// 计算方程的斜率
/// </summary>
/// <param name="vector1">方程的第一个点</param>
/// <param name="vector2">方程的第二个点</param>
/// <returns>得到方程的斜率</returns>
private static double Derivative(Vector3 vector1, Vector3 vector2)
{
double k = double.MaxValue;
if (vector2.X != vector1.X)
k = (vector2.Y - vector1.Y) / (vector2.X - vector1.X);
return k;
}
/// <summary>
/// 提取特征点
/// </summary>
/// <param name="points">点列</param>
/// <returns>Vector3</returns>
private static Vector3[] CharacterPoints(Vector3[] points)
{
List<Vector3> characterP = new List<Vector3>();//保存特征点
double[] k = new double[points.Length - 1];//保存斜率
for (int i = 0; i < points.Length - 1; i++)
{
Vector3 pointCurr = points[i];
Vector3 pointNext = points[i + 1];
k[i] = Derivative(pointCurr, pointNext);
}
characterP.Add(points[0]);//加上第一个点
for (int i = 0; i < k.Length - 1; i++)
{
if ((k[i] * k[i + 1]) <= 0)
characterP.Add(points[i + 1]);
}
characterP.Add(points[points.Length - 1]);//加上最后一个点
Vector3[] pointTemp = new Vector3[characterP.Count];
characterP.CopyTo(pointTemp, 0);
return pointTemp;
}
/// <summary>
///
/// </summary>
/// <param name="uu"></param>
/// <param name="controlP"></param>
/// <returns></returns>
private static double bezier3funcX(double uu, Vector3[] controlP)
{
double part0 = controlP[0].X * uu * uu * uu;
double part1 = 3 * controlP[1].X * uu * uu * (1 - uu);
double part2 = 3 * controlP[2].X * uu * (1 - uu) * (1 - uu);
double part3 = controlP[3].X * (1 - uu) * (1 - uu) * (1 - uu);
return part0 + part1 + part2 + part3;
}
/// <summary>
///
/// </summary>
/// <param name="uu"></param>
/// <param name="controlP"></param>
/// <returns></returns>
private static double bezier3funcY(double uu, Vector3[] controlP)
{
double part0 = controlP[0].Y * uu * uu * uu;
double part1 = 3 * controlP[1].Y * uu * uu * (1 - uu);
double part2 = 3 * controlP[2].Y * uu * (1 - uu) * (1 - uu);
double part3 = controlP[3].Y * (1 - uu) * (1 - uu) * (1 - uu);
return part0 + part1 + part2 + part3;
}
/// <summary>
///
/// </summary>
/// <param name="uu"></param>
/// <param name="controlP"></param>
/// <returns></returns>
private static double bezier3funcZ(double uu, Vector3[] controlP)
{
double part0 = controlP[0].Z * uu * uu * uu;
double part1 = 3 * controlP[1].Z * uu * uu * (1 - uu);
double part2 = 3 * controlP[2].Z * uu * (1 - uu) * (1 - uu);
double part3 = controlP[3].Z * (1 - uu) * (1 - uu) * (1 - uu);
return part0 + part1 + part2 + part3;
}
#endregion
#region 圆滑折线拐点
/// <summary>
/// 圆滑不闭合折线之间的角
/// </summary>
/// <param name="points">构成折线的点数组</param>
/// <returns>Vector3数组</returns>
public static Vector3[] OpenInflexion(Vector3[] points)
{
return OpenInflexion(points, 0.15, 0.1);
}
/// <summary>
/// 圆滑不闭合折线之间的角
/// </summary>
/// <param name="points">构成折线的点数组</param>
/// <param name="pos">直线开始圆滑的位置,取值在0-0.5之间,超过0.5两直线之间会有交叉</param>
/// <returns>Vector3数组</returns>
public static Vector3[] OpenInflexion(Vector3[] points, double pos)
{
return OpenInflexion(points, pos, 0.1);
}
/// <summary>
/// 圆滑不闭合折线之间的角
/// </summary>
/// <param name="points">构成折线的点数组</param>
/// <param name="pos">直线开始圆滑的位置,取值在0-0.5之间,超过0.5两直线之间会有交叉</param>
/// <param name="t">圆润参数,在0-1之间,值越小越圆滑</param>
/// <returns>Vector3数组</returns>
public static Vector3[] OpenInflexion(Vector3[] points, double pos, double t)
{
if (points == null || points.Length == 0) return new Vector3[0];
//保存不在一条直线上的所有点
List<Vector3> characterP = new List<Vector3>();
double[] k = new double[points.Length - 1];//保存斜率
for (int i = 0; i < points.Length - 1; i++)
{
Vector3 pointCurr = points[i];
Vector3 pointNext = points[i + 1];
k[i] = Derivative(pointCurr, pointNext);
}
characterP.Add(points[0]);//加上第一个点
for (int i = 0; i < k.Length - 1; i++)
{
characterP.Add(points[i + 1]);
if ((k[i] - k[i + 1] == 0))
{
int lastValue = characterP.Count - 1;
characterP.RemoveAt(lastValue);
}
}
characterP.Add(points[points.Length - 1]);//加上最后一个点
//根据pos值计算拐点在直线上的位置
Vector3 tempPoint = null;
List<Vector3> getPoints = new List<Vector3>();
Vector3 dir = null, normalize = null;
for (int i = 0; i < characterP.Count - 1; i++)
{
getPoints.Add(characterP[i]);
Vector3 pointCurr = characterP[i];
Vector3 pointNext = characterP[i + 1];
dir = pointNext - pointCurr;
double len = dir.Length();
normalize = dir.NormalisedCopy();//.Normalise();//.Normalize();
tempPoint = pointCurr + normalize * (len * pos);
getPoints.Add(tempPoint);
tempPoint = pointCurr + normalize * (len * (1 - pos));
getPoints.Add(tempPoint);
}
getPoints.Add(characterP[characterP.Count - 1]);//加上最后一个点
getPoints.RemoveAt(1);
getPoints.RemoveAt(getPoints.Count - 2);
//处理拐点,使拐点变圆滑
List<Vector3> pointss = new List<Vector3>();
Vector3[] controlPoints = new Vector3[3];
Vector3 point = null;
pointss.Add(getPoints[0]);
for (int i = 0; i < characterP.Count - 2; i++)
{//获取控制点
controlPoints[0] = getPoints[i * 3 + 1];
controlPoints[1] = getPoints[i * 3 + 2];
controlPoints[2] = getPoints[i * 3 + 3];
double u = 0;
while (u <= 1)
{
point = Bezier2Func(u, controlPoints);
if (pointss[pointss.Count - 1].X != point.X || pointss[pointss.Count - 1].Y != point.Y)
{
pointss.Add(point);
}
if (u == 1) break;
u += t;
if (u > 1)
{
u = 1;
point = Bezier2Func(u, controlPoints);
if (pointss[pointss.Count - 1].X != point.X || pointss[pointss.Count - 1].Y != point.Y)
{
pointss.Add(point);
}
break;
}
}
}
pointss.Add(getPoints[getPoints.Count - 1]);
Vector3[] tempPs = new Vector3[pointss.Count];
pointss.CopyTo(tempPs, 0);
return tempPs;
}
/// <summary>
/// 圆滑闭合折线之间的角
/// </summary>
/// <param name="pointArray">构成折线的点数组</param>
/// <returns>Vector3数组</returns>
public static Vector3[] CloseInflexion(Vector3[] pointArray)
{
return CloseInflexion(pointArray, 0.15, 0.1);
}
/// <summary>
/// 圆滑闭合折线之间的角
/// </summary>
/// <param name="pointArray">构成折线的点数组</param>
/// <param name="pos">直线开始圆滑的位置,取值在0-0.5之间,超过0.5两直线之间会有交叉</param>
/// <returns>Vector3数组</returns>
public static Vector3[] CloseInflexion(Vector3[] pointArray, double pos)
{
return CloseInflexion(pointArray, pos, 0.1);
}
/// <summary>
/// 圆滑闭合折线之间的角
/// </summary>
/// <param name="pointArray">构成折线的点数组</param>
/// <param name="pos">直线开始圆滑的位置,取值在0-0.5之间,超过0.5两直线之间会有交叉</param>
/// <param name="t">圆润参数,在0-1之间,值越小越圆滑</param>
/// <returns>Vector3数组</returns>
public static Vector3[] CloseInflexion(Vector3[] pointArray, double pos, double t)
{
Vector3[] points = null;
if (pointArray[0].X != pointArray[pointArray.Length - 1].X && pointArray[0].Y != pointArray[pointArray.Length - 1].Y)
{
points = new Vector3[pointArray.Length + 1];
pointArray.CopyTo(points, 0);
points[pointArray.Length] = points[0];
}
else
{
points = pointArray;
}
//保存不在一条直线上的所有点
List<Vector3> characterP = new List<Vector3>();
double[] k = new double[points.Length - 1];//保存斜率
for (int i = 0; i < points.Length - 1; i++)
{
Vector3 pointCurr = points[i];
Vector3 pointNext = points[i + 1];
k[i] = Derivative(pointCurr, pointNext);
}
characterP.Add(points[0]);//加上第一个点
for (int i = 0; i < k.Length - 1; i++)
{
characterP.Add(points[i + 1]);
if ((k[i] - k[i + 1] == 0))
{
int lastValue = characterP.Count - 1;
characterP.RemoveAt(lastValue);
}
}
characterP.Add(points[points.Length - 1]);//加上最后一个点
//根据pos值计算拐点在直线上的位置
Vector3 tempPoint = null;
List<Vector3> getPoints = new List<Vector3>();
Vector3 dir = null, normalize = null;
for (int i = 0; i < characterP.Count - 1; i++)
{
getPoints.Add(characterP[i]);
Vector3 pointCurr = characterP[i];
Vector3 pointNext = characterP[i + 1];
dir = pointNext - pointCurr;
double len = dir.Length();
normalize = dir.NormalisedCopy();//.Normalize();
tempPoint = pointCurr + normalize * (len * pos);
getPoints.Add(tempPoint);
tempPoint = pointCurr + normalize * (len * (1 - pos));
getPoints.Add(tempPoint);
}
//处理拐点,使拐点变圆滑
List<Vector3> pointss = new List<Vector3>();
Vector3[] controlPoints = new Vector3[3];
Vector3 point = null;
for (int i = 0; i < characterP.Count - 1; i++)
{
//获取控制点
if (i == characterP.Count - 2)
{
controlPoints[0] = getPoints[getPoints.Count - 1];
controlPoints[1] = getPoints[0];
controlPoints[2] = getPoints[1];
}
else
{
controlPoints[0] = getPoints[i * 3 + 2];
controlPoints[1] = getPoints[i * 3 + 3];
controlPoints[2] = getPoints[i * 3 + 4];
}
double u = 0;
while (u <= 1)
{
point = Bezier2Func(u, controlPoints);
pointss.Add(point);
//u的步长决定曲线的疏密
if (u == 1) break;
u += t;
if (u > 1)
{
u = 1;
point = Bezier2Func(u, controlPoints);
pointss.Add(point);
break;
}
}
}
pointss.Add(getPoints[2]);
Vector3[] tempPs = new Vector3[pointss.Count];
pointss.CopyTo(tempPs, 0);
return tempPs;
}
/// <summary>
///
/// </summary>
/// <param name="uu"></param>
/// <param name="controlP"></param>
/// <returns></returns>
private static Vector3 Bezier2Func(double uu, Vector3[] controlP)
{
double x0, y0, z0;
double partX0 = controlP[0].X * (1 - uu) * (1 - uu);
double partX1 = 2 * controlP[1].X * uu * (1 - uu);
double partX2 = controlP[2].X * uu * uu;
x0 = partX0 + partX1 + partX2;
double partY0 = controlP[0].Y * (1 - uu) * (1 - uu);
double partY1 = 2 * controlP[1].Y * uu * (1 - uu);
double partY2 = controlP[2].Y * uu * uu;
y0 = partY0 + partY1 + partY2;
double partZ0 = controlP[0].Z * (1 - uu) * (1 - uu);
double partZ1 = 2 * controlP[1].Z * uu * (1 - uu);
double partZ2 = controlP[2].Z * uu * uu;
z0 = partZ0 + partZ1 + partZ2;
Vector3 point = new Vector3(x0, y0, z0);
return point;
}
#endregion
#region 私有函数
/// <summary>
///
/// </summary>
/// <param name="controlPoints"></param>
/// <param name="t"></param>
/// <returns></returns>
private static Vector3[] CreateBezier(Vector3[] controlPoints, double t)
{
List<Vector3> getPoints = new List<Vector3>();
Vector3 point = null;
int n = controlPoints.Length - 1;
double u = 0;
while (u <= 1)
{
point = Bezier(n, u, controlPoints);
getPoints.Add(point);
if (u == 1) break;
u += t;
if (u > 1)
{
u = 1;
point = Bezier(n, u, controlPoints);
getPoints.Add(point);
break;
}
}
Vector3[] points = new Vector3[getPoints.Count];
getPoints.CopyTo(points, 0);
return points;
}
/// <summary>
///
/// </summary>
/// <param name="n"></param>
/// <param name="t"></param>
/// <param name="controlPoints"></param>
/// <returns></returns>
private static Vector3 Bezier(int n, double t, Vector3[] controlPoints)
{
double x0 = 0, y0 = 0, z0 = 0;
Vector3 point = null;
for (int i = 0; i < controlPoints.Length; i++)
{
point = Bernstein(i, n, t, controlPoints[i]);
x0 = x0 + point.X;
y0 = y0 + point.Y;
z0 = z0 + point.Z;
}
x0 = System.Math.Round(x0, 15);
y0 = System.Math.Round(y0, 15);
z0 = System.Math.Round(z0, 15);
point = new Vector3(x0, y0, z0);
return point;
}
/// <summary>
/// Bi,n(t)是n次Bernstein基函数。
/// </summary>
/// <param name="i"></param>
/// <param name="n"></param>
/// <param name="t"></param>
/// <param name="controlPoint"></param>
/// <returns></returns>
private static Vector3 Bernstein(int i, int n, double t, Vector3 controlPoint)
{
double N = 1, T = 1;
Vector3 point = new Vector3();
int j, Nn = 1, Ni = 1, Nni = 1;
for (j = n; j >= 1; j--) Nn = Nn * j;
for (j = i; j >= 1; j--) Ni = Ni * j;
for (j = n - i; j >= 1; j--) Nni = Nni * j;
N = Nn / (double)(Ni * Nni);
T = System.Math.Pow(t, i) * System.Math.Pow((1 - t), (n - i));
point.X = controlPoint.X * N * T;
point.Y = controlPoint.Y * N * T;
point.Z = controlPoint.Z * N * T;
return point;
}
#endregion