PointF[] Spline2Bezier(PointF[] points, int start, int num, bool closed, float tension)
private static PointF[] Spline2Bezier(PointF[] points, int start, int num, bool closed, float tension)
{
var res = new ArrayList();
int len = points.Length - 1;
_ = res.Add(points[0]);
_ = res.Add(ControlPoint(points[1], points[0], tension));
for (int i = 1; i < len; ++i)
{
PointF[] pts = ControlPoints(points[i - 1], points[i + 1], points[i], tension);
_ = res.Add(pts[0]);
_ = res.Add(points[i]);
_ = res.Add(pts[1]);
}
_ = res.Add(ControlPoint(points[len - 1], points[len], tension));
_ = res.Add(points[len]);
if (closed)
{
//adjust rh cp of point 0
PointF[] pts = ControlPoints(points[len], points[1], points[0], tension);
res[1] = pts[1];
//adjust lh cp of point l and add rh cp
pts = ControlPoints(points[len - 1], points[0], points[len], tension);
res[res.Count - 2] = pts[0];
_ = res.Add(pts[1]);
//add new end point and its lh cp
pts = ControlPoints(points[len], points[1], points[0], tension);
_ = res.Add(pts[0]);
_ = res.Add(points[0]);
return (PointF[])res.ToArray(typeof(PointF));
}
else
{
var subset = new ArrayList();
for (int i = start * 3; i < (start + num) * 3; ++i)
_ = subset.Add(res[i]);
_ = subset.Add(res[(start + num) * 3]);
return (PointF[])subset.ToArray(typeof(PointF));
}
}
其中:ControlPoints方法如下:
#region ControlPoint
private static PointF ControlPoint(PointF prev, PointF current, float t)
{
var v = new PointF(prev.X - current.X, prev.Y - current.Y);
float vlen = (float)Math.Sqrt((v.X * v.X) + (v.Y * v.Y));
v.X /= (float)Math.Sqrt(vlen / (10 * t * t));
v.Y /= (float)Math.Sqrt(vlen / (10 * t * t));
return new PointF(current.X + v.X, current.Y + v.Y);
}
private static PointF[] ControlPoints(PointF prev, PointF next, PointF current, float t)
{
//points to vectors
var lv = new PointF(prev.X - current.X, prev.Y - current.Y);
var rv = new PointF(next.X - current.X, next.Y - current.Y);
var nlv = new PointF(lv.X - rv.X, lv.Y - rv.Y);
var nrv = new PointF(rv.X - lv.X, rv.Y - lv.Y);
float nlvlen = (float)Math.Sqrt((nlv.X * nlv.X) + (nlv.Y * nlv.Y));
nlv.X /= (float)Math.Sqrt(nlvlen / (10 * t * t));
nlv.Y /= (float)Math.Sqrt(nlvlen / (10 * t * t));
float nrvlen = (float)Math.Sqrt((nrv.X * nrv.X) + (nrv.Y * nrv.Y));
nrv.X /= (float)Math.Sqrt(nrvlen / (10 * t * t));
nrv.Y /= (float)Math.Sqrt(nrvlen / (10 * t * t));
var ret = new PointF[2];
ret[0] = new PointF(current.X + nlv.X, current.Y + nlv.Y);
ret[1] = new PointF(current.X + nrv.X, current.Y + nrv.Y);
return ret;
}
#endregion ControlPoint
调用方法:
#region DrawCurve
/// <summary>
/// Implemented. The <c>DrawCurve</c> functions emulate GDI behavior by drawing a coaligned cubic bezier. This seems to produce
/// a very good approximation so probably GDI+ does the same.
/// </summary>
public void DrawCurve(Pen pen, PointF[] points)
{
PointF[] pts = Spline2Bezier(points, 0, points.Length - 1, false, .5f);
DrawBeziers(pen, pts);
}
/// <summary>
/// Implemented
/// </summary>
public void DrawCurve(Pen pen, PointF[] points, float tension)
{
PointF[] pts = Spline2Bezier(points, 0, points.Length - 1, false, tension);
DrawBeziers(pen, pts);
}
/// <summary>
/// Implemented
/// </summary>
public void DrawCurve(Pen pen, PointF[] points, int offset, int numberOfSegments)
{
PointF[] pts = Spline2Bezier(points, offset, numberOfSegments, false, .5f);
DrawBeziers(pen, pts);
}
/// <summary>
/// Implemented
/// </summary>
public void DrawCurve(Pen pen, PointF[] points, int offset, int numberOfSegments, float tension)
{
PointF[] pts = Spline2Bezier(points, offset, numberOfSegments, false, tension);
DrawBeziers(pen, pts);
}
/// <summary>
/// Implemented
/// </summary>
public void DrawCurve(Pen pen, Point[] points)
{
PointF[] pts = Spline2Bezier(Point2PointF(points), 0, points.Length - 1, false, .5f);
DrawBeziers(pen, pts);
}
/// <summary>
/// Implemented
/// </summary>
public void DrawCurve(Pen pen, Point[] points, float tension)
{
PointF[] pts = Spline2Bezier(Point2PointF(points), 0, points.Length - 1, false, tension);
DrawBeziers(pen, pts);
}
/// <summary>
/// Implemented
/// </summary>
public void DrawCurve(Pen pen, Point[] points, int offset, int numberOfSegments, float tension)
{
PointF[] pts = Spline2Bezier(Point2PointF(points), offset, numberOfSegments, false, tension);
DrawBeziers(pen, pts);
}
#endregion DrawCurve
另:将GDI+中的DrawArc方法转为Svg中的Path路径:
public static string GdiPlusArc2SvgPath(float x, float y, float width, float height, float startAngle, float sweepAngle, bool pie)标签:曲线,PointF,C#,res,float,points,net,pts,Math From: https://blog.51cto.com/u_15983015/6088280
{
int longArc = 0;
var start = new PointF();
var end = new PointF();
var center = new PointF(x + (width / 2f), y + (height / 2f));
startAngle = startAngle / 360f * 2f * (float)Math.PI;
sweepAngle = sweepAngle / 360f * 2f * (float)Math.PI;
sweepAngle += startAngle;
#pragma warning disable IDE0180
if (sweepAngle > startAngle)
{
float temp = startAngle;
startAngle = sweepAngle;
sweepAngle = temp;
}
#pragma warning restore IDE0180
if (sweepAngle - startAngle > Math.PI || startAngle - sweepAngle > Math.PI) longArc = 1;
start.X = ((float)Math.Cos(startAngle) * (width / 2f)) + center.X;
start.Y = ((float)Math.Sin(startAngle) * (height / 2f)) + center.Y;
end.X = ((float)Math.Cos(sweepAngle) * (width / 2f)) + center.X;
end.Y = ((float)Math.Sin(sweepAngle) * (height / 2f)) + center.Y;
string s = "M " + start.X.ToString("F", CultureInfo.InvariantCulture) + "," + start.Y.ToString("F", CultureInfo.InvariantCulture) +
" A " + (width / 2f).ToString("F", CultureInfo.InvariantCulture) + " " + (height / 2f).ToString("F", CultureInfo.InvariantCulture) + " " +
"0 " + longArc.ToString() + " 0 " + end.X.ToString("F", CultureInfo.InvariantCulture) + " " + end.Y.ToString("F", CultureInfo.InvariantCulture);
if (pie)
{
s += " L " + center.X.ToString("F", CultureInfo.InvariantCulture) + "," + center.Y.ToString("F", CultureInfo.InvariantCulture);
s += " L " + start.X.ToString("F", CultureInfo.InvariantCulture) + "," + start.Y.ToString("F", CultureInfo.InvariantCulture);
}
return s;
}