做一条过N点的平滑曲线
引用自http://www.cnblogs.com/muxue/archive/2010/06/23/1763886.html
当然有一个小的速度上优化技巧,令d1=d2这样计算复杂度会减少,但是曲线的拐角会比较丑。
public class BezierCurves
{
/// <summary>
/// create control point
/// </summary>
/// <param name="points"></param>
/// <param name="smoothValue"></param>
/// <returns></returns>
private static Point[] CalculControlPoint(Point[] points, double smoothValue)
{
// Assume we need to calculate the control
// points between (points[1].X,points[1].Y) and (points[2].X,points[2].Y).
// Then points[0].X,points[0].Y - the previous vertex,
// points[3].X,points[3].Y - the next one.
double xc1 = (points[0].X + points[1].X) / 2.0;
double yc1 = (points[0].Y + points[1].Y) / 2.0;
double xc2 = (points[1].X + points[2].X) / 2.0;
double yc2 = (points[1].Y + points[2].Y) / 2.0;
double xc3 = (points[2].X + points[3].X) / 2.0;
double yc3 = (points[2].Y + points[3].Y) / 2.0;
double len1 = Math.Sqrt((points[1].X - points[0].X) * (points[1].X - points[0].X) + (points[1].Y - points[0].Y) * (points[1].Y - points[0].Y));
double len2 = Math.Sqrt((points[2].X - points[1].X) * (points[2].X - points[1].X) + (points[2].Y - points[1].Y) * (points[2].Y - points[1].Y));
double len3 = Math.Sqrt((points[3].X - points[2].X) * (points[3].X - points[2].X) + (points[3].Y - points[2].Y) * (points[3].Y - points[2].Y));
double k1 = len1 / (len1 + len2);
double k2 = len2 / (len2 + len3);
double xm1 = xc1 + (xc2 - xc1) * k1;
double ym1 = yc1 + (yc2 - yc1) * k1;
double xm2 = xc2 + (xc3 - xc2) * k2;
double ym2 = yc2 + (yc3 - yc2) * k2;
Point[] outPoints = new Point[4];
// Resulting control points. Here smooth_value is mentioned
// above coefficient K whose value should be in range [0...1].
outPoints[0] = points[1];
outPoints[1].X = (float)(xm1 + (xc2 - xm1) * smoothValue + points[1].X - xm1);
outPoints[1].Y = (float)(ym1 + (yc2 - ym1) * smoothValue + points[1].Y - ym1);
outPoints[2].X = (float)(xm2 + (xc2 - xm2) * smoothValue + points[2].X - xm2);
outPoints[2].Y = (float)(ym2 + (yc2 - ym2) * smoothValue + points[2].Y - ym2);
outPoints[3] = points[2];
return outPoints;
}
// Number of intermediate points between two source ones,
// Actually, this value should be calculated in some way,
// Obviously, depending on the real length of the curve.
// But I don't know any elegant and fast solution for this
// problem.
private static List<Point> Curve4(Point[] points, int numSteps = 20)
{
Trace.Assert(points.Length == 4);
double x1 = points[0].X;
double y1 = points[0].Y; //Anchor1
double x2 = points[1].X;
double y2 = points[1].Y; //Control1
double x3 = points[2].X;
double y3 = points[2].Y; //Control2
double x4 = points[3].X;
double y4 = points[3].Y; //Anchor2
double dx1 = x2 - x1;
double dy1 = y2 - y1;
double dx2 = x3 - x2;
double dy2 = y3 - y2;
double dx3 = x4 - x3;
double dy3 = y4 - y3;
double subdiv_step = 1.0 / (numSteps + 1);
double subdiv_step2 = subdiv_step * subdiv_step;
double subdiv_step3 = subdiv_step * subdiv_step * subdiv_step;
double pre1 = 3.0 * subdiv_step;
double pre2 = 3.0 * subdiv_step2;
double pre4 = 6.0 * subdiv_step2;
double pre5 = 6.0 * subdiv_step3;
double tmp1x = x1 - x2 * 2.0 + x3;
double tmp1y = y1 - y2 * 2.0 + y3;
double tmp2x = (x2 - x3) * 3.0 - x1 + x4;
double tmp2y = (y2 - y3) * 3.0 - y1 + y4;
double fx = x1;
double fy = y1;
double dfx = (x2 - x1) * pre1 + tmp1x * pre2 + tmp2x * subdiv_step3;
double dfy = (y2 - y1) * pre1 + tmp1y * pre2 + tmp2y * subdiv_step3;
double ddfx = tmp1x * pre4 + tmp2x * pre5;
double ddfy = tmp1y * pre4 + tmp2y * pre5;
double dddfx = tmp2x * pre5;
double dddfy = tmp2y * pre5;
int step = numSteps;
List<Point> list = new List<Point>();
// Suppose, we have some abstract object Polygon which
// has method AddVertex(x, y), similar to LineTo in
// many graphical APIs.
// Note, that the loop has only operation add!
while (step-- > 0)
{
fx += dfx;
fy += dfy;
dfx += ddfx;
dfy += ddfy;
ddfx += dddfx;
ddfy += dddfy;
list.Add(new Point(fx, fy));
}
list.Add(points[3]); // Last step must go exactly to x4, y4
return list;
}
public static List<Point> GetPolyline(List<Point> list, double smoothValue, int numSteps, bool isLoop, bool isOpen)
{
List<Point> temp = list.ToList();
if (isLoop)
{
temp.Insert(0, list.Last());
temp.Add(temp[1]);
temp.Add(temp[2]);
}
else
{
temp.Insert(0, temp.First());
temp.Add(temp.Last());
temp.Add(temp[0]);
temp.Add(temp[0]);
}
List<Point> result = new List<Point>();
result.Add(list[0]); // first point
for (int i = 0; i < (!isOpen ? list.Count : list.Count - 1); i++)
{
Point[] points = CalculControlPoint(temp.GetRange(i, 4).ToArray(), smoothValue);
result.AddRange(Curve4(points, numSteps));
}
return result;
}
}
smoothValue =0 就是单纯的直线,
loop是首尾相接,主要影响的是起点和终点是否平滑。
open代表是否开闭。
没怎么优化,基本就是抄了一遍引用链接里的代码。