C#绘制带控制点的Bezier曲线,用于点阵图像及矢量图形

【摘要】不借助第三方, 使用c# + GDI+进行SVG等绘图,绘制带控制点的Bezier曲线。可用于点阵图像及矢量图形(如SVG)绘图。

先看效果:
(不知为何,已两次上传图片,无法显示,求助csdn)

图注:使用方法二绘制。


方法一:
/// <summary>
/// Bezier样条曲线
/// </summary>
public static class BezierSpline
{
	/// <summary>
	/// Get open-ended Bezier Spline Control Points.
	/// </summary>
	/// <param name="knots">Input Knot Bezier spline points.</param>
	/// <param name="firstControlPoints">Output First Control points
	/// array of knots.Length - 1 length.</param>
	/// <param name="secondControlPoints">Output Second Control points
	/// array of knots.Length - 1 length.</param>
	/// <exception cref="ArgumentNullException"><paramref name="knots"/>
	/// parameter must be not null.</exception>
	/// <exception cref="ArgumentException"><paramref name="knots"/>
	/// array must contain at least two points.</exception>
	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);
		}
	}

	/// <summary>
	/// Solves a tridiagonal system for one of coordinates (x or y)
	/// of first Bezier control points.
	/// </summary>
	/// <param name="rhs">Right hand side vector.</param>
	/// <returns>Solution vector.</returns>
	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下载 

猜你喜欢

转载自blog.csdn.net/johnsuna/article/details/125446942
今日推荐