【摘要】不借助第三方, 使用c# + GDI+进行SVG等绘图,绘制带控制点的Bezier曲线。可用于点阵图像及矢量图形(如SVG)绘图。 先看效果: (不知为何,已两次上传图片,无法显示,求助csdn) 图注:使用方法二绘制。 方法一: ////// Bezier样条曲线 /// public static class BezierSpline { ////// Get open-ended Bezier Spline Control Points. /// /// Input Knot Bezier spline points. /// Output First Control points /// array of knots.Length - 1 length. /// Output Second Control points /// array of knots.Length - 1 length. ////// /// parameter must be not null. public static void GetCurveControlPoints(Point[] knots, out Point[] firstControlPoints, out Point[] secondControlPoints) { if (knots == null) throw new ArgumentNullException("knots"); int n = knots.Length - 1; if (n < 1) throw new ArgumentException ("At least two knot points required", "knots"); if (n == 1) { // Special case: Bezier curve should be a straight line. firstControlPoints = new Point[1]; // 3P1 = 2P0 + P3 firstControlPoints[0].X = (2 * knots[0].X + knots[1].X) / 3; firstControlPoints[0].Y = (2 * knots[0].Y + knots[1].Y) / 3; secondControlPoints = new Point[1]; // P2 = 2P1 – P0 secondControlPoints[0].X = 2 * firstControlPoints[0].X - knots[0].X; secondControlPoints[0].Y = 2 * firstControlPoints[0].Y - knots[0].Y; return; } // Calculate first Bezier control points // Right hand side vector double[] rhs = new double[n]; // Set right hand side X values for (int i = 1; i < n - 1; ++i) rhs[i] = 4 * knots[i].X + 2 * knots[i + 1].X; rhs[0] = knots[0].X + 2 * knots[1].X; rhs[n - 1] = (8 * knots[n - 1].X + knots[n].X) / 2.0; // Get first control points X-values double[] x = GetFirstControlPoints(rhs); // Set right hand side Y values for (int i = 1; i < n - 1; ++i) rhs[i] = 4 * knots[i].Y + 2 * knots[i + 1].Y; rhs[0] = knots[0].Y + 2 * knots[1].Y; rhs[n - 1] = (8 * knots[n - 1].Y + knots[n].Y) / 2.0; // Get first control points Y-values double[] y = GetFirstControlPoints(rhs); // Fill output arrays. firstControlPoints = new Point[n]; secondControlPoints = new Point[n]; for (int i = 0; i < n; ++i) { // First control point firstControlPoints[i] = new Point(x[i], y[i]); // Second control point if (i < n - 1) secondControlPoints[i] = new Point(2 * knots [i + 1].X - x[i + 1], 2 * knots[i + 1].Y - y[i + 1]); else secondControlPoints[i] = new Point((knots [n].X + x[n - 1]) / 2, (knots[n].Y + y[n - 1]) / 2); } } /// /// array must contain at least two points. /// Solves a tridiagonal system for one of coordinates (x or y) /// of first Bezier control points. /// /// Right hand side vector. ///Solution vector. private static double[] GetFirstControlPoints(double[] rhs) { int n = rhs.Length; double[] x = new double[n]; // Solution vector. double[] tmp = new double[n]; // Temp workspace. double b = 2.0; x[0] = rhs[0] / b; for (int i = 1; i < n; i++) // Decomposition and forward substitution. { tmp[i] = 1 / b; b = (i < n - 1 ? 4.0 : 3.5) - tmp[i]; x[i] = (rhs[i] - x[i - 1]) / b; } for (int i = 1; i < n; i++) x[n - i - 1] -= tmp[n - i] * x[n - i]; // Backsubstitution. return x; } }
方法二:
private void DrawCurve(Graphics g, PointF[] points, float tension)
{
int n=points.Length;
Pen rPen = new Pen(Color.Red, 2f);
Pen blPen= new Pen(Color.Blue, 1f);
Pen bzPen = new Pen(Color.DarkGoldenrod, 2f);
for (int i = 0; i < n; ++i)
{
// draw segment points[i] - points[(i + 1) % n]
var pPrev1 = points[(i - 1 + n) % n];
var p1 = points[i];
var p2 = points[(i + 1) % n];
var pAfter2 = points[(i + 2) % n];
// tangents 切线控制点
var t1 = new PointF(tension * (p2.X - pPrev1.X), tension * (p2.Y - pPrev1.Y));
var t2 = new PointF(tension * (pAfter2.X - p1.X), tension * (pAfter2.Y - p1.Y));
// interior Bezier control points
var c1 = new PointF(p1.X + t1.X / 3.0f, p1.Y + t1.Y / 3.0f);
var c2 = new PointF(p2.X - t2.X / 3.0f, p2.Y - t2.Y / 3.0f);
//画贝塞尔曲线
g.DrawBezier(bzPen, p1, c1, c2, p2);
//画关键点到切线控制点的直线
g.DrawLine(blPen, p1, c1);
g.DrawEllipse(rPen, p1.X - 2, p1.Y - 2, 4, 4);
g.DrawEllipse(rPen, c1.X - 2, c1.Y - 2, 4, 4);
g.DrawLine(blPen, p2, c2);
g.DrawEllipse(rPen, p2.X - 2, p2.Y - 2, 4, 4);
g.DrawEllipse(rPen, c2.X - 2, c2.Y - 2, 4, 4);
g.FillEllipse(new SolidBrush(Color.Green), new RectangleF(p1.X-2, p1.Y-2, 4, 4));
}
}
方法二的调用方法:
//这里使用的是Panel上绘图,其他控件(如PictureBox)道理一样。
Graphics g = pnlWorkArea.CreateGraphics();
g.Clear(Color.White);
g.CompositingQuality = CompositingQuality.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.HighQuality;
g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
PointF[] points = { new PointF(568,200),new PointF(168,110),new PointF(60,186),new PointF(300,191),new PointF(600,300),new PointF(800,431),new PointF(300,650), new PointF(568, 200) };
float tension=0.68f;
DrawCurve(g, points, tension);
这里有一个不错的链接:
C# GraphicsPath AddBeziers(params System.Drawing.Point[] points)
C# GraphicsPath AddBeziers(System.Drawing.PointF[] points)
源码也可以在此下载:C#带控制点的贝塞尔Bezier曲线算法(源码)-C#文档类资源-CSDN下载