C# .Net在DrawClosedCurve、DrawCurve和DrawBezier之间的转换
我需要在DrawClosedCurve和DrawBezier之间进行转换。例如,如果我有一条闭合曲线:C# .Net在DrawClosedCurve、DrawCurve和DrawBezier之间的转换,c#,.net,graphics,bezier,spline,C#,.net,Graphics,Bezier,Spline,我需要在DrawClosedCurve和DrawBezier之间进行转换。例如,如果我有一条闭合曲线: Point point1 = new Point(100, 100); Point point2 = new Point(200, 50); Point point3 = new Point(250, 200); Point point4 = new Point(50, 150); Point[] points = {point1, point2, point3, point4}; e.Gr
Point point1 = new Point(100, 100);
Point point2 = new Point(200, 50);
Point point3 = new Point(250, 200);
Point point4 = new Point(50, 150);
Point[] points = {point1, point2, point3, point4};
e.Graphics.DrawClosedCurve(pen, points, tension, FillMode);
我需要能够定义与此形状匹配的[四条?]贝塞尔曲线,即给定上面的闭合曲线,我需要为每个线段找到两个定位点。例如,第一段的贝塞尔曲线:
e.Graphics.DrawBezier(pen, point1, anchor1, anchor2, point2)
同样,给定bezier曲线,我需要定义一条匹配的闭合曲线
我提到DrawCurve是因为我不明白为什么这两个函数不能产生相同的结果:
e.Graphics.DrawClosedCurve(pen, points, tension, FillMode);
e.Graphics.DrawCurve(pen, {point1, point2, point3, point4, point1}, tension);
编辑-或者什么会使后者产生相同的结果
我理解,但我不理解基数,我假设它是所使用的样条曲线。我也不明白给出的答案。让我先回答你的第二个问题 我不明白为什么这两个函数不能产生相同的结果 基数样条曲线需要相邻点来计算切线。因此,您需要在两侧再添加一点:
e.Graphics.DrawCurve(pen, new PointF[] { point4, point1, point2, point3, point4, point1, point2}, tension);
当然,这也将绘制这些附加段。如果忽略这些,则曲线完全相同。除此之外,无法使DrawCurve
和DrawClosedCurve
产生相同的输出:
在此图中,DrawCurve
使用蓝色笔,DrawClosedCurve
使用红色笔。除了另外两条蓝色线段外,曲线是相等的
现在,对于贝塞尔曲线:
显然,两个点之间的每个线段都需要一条贝塞尔曲线
for(int i = 0; i < points.Length; ++i)
现在,我们可以使用基数样条曲线的定义计算切线:
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));
最后转换成贝塞尔曲线。端点保持不变。内部控制点需要反映端点切线,并按系数1/3
缩放(基于Hermite和Bezier曲线之间的关系):
最后画出曲线:
e.Graphics.DrawBezier(Pens.Purple, p1, c1, c2, p2);
以下是完整的代码:
for(int i = 0; i < points.Length; ++i)
{
// draw segment points[i] - points[(i + 1) % n]
var pPrev1 = points[(i - 1 + points.Length) % points.Length];
var p1 = points[i];
var p2 = points[(i + 1) % points.Length];
var pAfter2 = points[(i + 2) % points.Length];
// 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);
e.Graphics.DrawBezier(Pens.Purple, p1, c1, c2, p2);
}
显然,传递到
DrawClosedCurve
的点是端点{p1、p2、p3}
。我们唯一的自由度是张力参数。贝塞尔曲线具有更多的自由度(所有内部控制点各有两个维度)。因此,可能有一个独特的张力参数适用于所有人,但可能性很小。计算张力参数需要执行与上述非常类似的操作。您可以设置一个优化问题,以找到最精确地类似于贝塞尔曲线的张力参数。但这是另一个问题的故事。让我先回答你的第二个问题
我不明白为什么这两个函数不能产生相同的结果
基数样条曲线需要相邻点来计算切线。因此,您需要在两侧再添加一点:
e.Graphics.DrawCurve(pen, new PointF[] { point4, point1, point2, point3, point4, point1, point2}, tension);
当然,这也将绘制这些附加段。如果忽略这些,则曲线完全相同。除此之外,无法使DrawCurve
和DrawClosedCurve
产生相同的输出:
在此图中,DrawCurve
使用蓝色笔,DrawClosedCurve
使用红色笔。除了另外两条蓝色线段外,曲线是相等的
现在,对于贝塞尔曲线:
显然,两个点之间的每个线段都需要一条贝塞尔曲线
for(int i = 0; i < points.Length; ++i)
现在,我们可以使用基数样条曲线的定义计算切线:
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));
最后转换成贝塞尔曲线。端点保持不变。内部控制点需要反映端点切线,并按系数1/3
缩放(基于Hermite和Bezier曲线之间的关系):
最后画出曲线:
e.Graphics.DrawBezier(Pens.Purple, p1, c1, c2, p2);
以下是完整的代码:
for(int i = 0; i < points.Length; ++i)
{
// draw segment points[i] - points[(i + 1) % n]
var pPrev1 = points[(i - 1 + points.Length) % points.Length];
var p1 = points[i];
var p2 = points[(i + 1) % points.Length];
var pAfter2 = points[(i + 2) % points.Length];
// 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);
e.Graphics.DrawBezier(Pens.Purple, p1, c1, c2, p2);
}
显然,传递到
DrawClosedCurve
的点是端点{p1、p2、p3}
。我们唯一的自由度是张力参数。贝塞尔曲线具有更多的自由度(所有内部控制点各有两个维度)。因此,可能有一个独特的张力参数适用于所有人,但可能性很小。计算张力参数需要执行与上述非常类似的操作。您可以设置一个优化问题,以找到最精确地类似于贝塞尔曲线的张力参数。但这是另一个问题的故事。如果它们产生相同的结果,就不需要有两个不同的函数。:-)不管怎样,你不了解基数样条曲线的Wiki页面中的哪一部分。特别是间隔(x_k,x_k+1)。我熟悉t∈ [0,1]。符号(…,x_k,x_k+1,…)用于定义三次Hermite样条曲线(这意味着它包含多个三次多项式段)。您可以将每个线段视为三次贝塞尔曲线,并将参数转换为t=(x-x_k)/(x_k+1-x_k)。如果它们产生相同的结果,则无需使用两个不同的函数。:-)不管怎样,你不了解基数样条曲线的Wiki页面中的哪一部分。特别是间隔(x_k,x_k+1)。我熟悉t∈ [0,1]。符号(…,x_k,x_k+1,…)用于定义三次Hermite样条曲线(这意味着它包含多个三次多项式段)。您可以将每个线段视为三次贝塞尔曲线,并将参数转换为t=(x-x_k)/(x_k+1-x_k)。感谢您的详细回答!既然您已经演示了基数样条线的工作原理,我将尝试优化张力参数。谢谢您的详细回答!既然已经演示了基数样条线的工作原理,我将尝试优化张力参数。