最简单的解决方案是设置张力:

绿色曲线以默认张力绘制,蓝色曲线设置为0.1f:
private void panel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.DrawLines(Pens.Red, points.ToArray());
e.Graphics.DrawCurve(Pens.Green, points.ToArray());
e.Graphics.DrawCurve(Pens.Blue, points.ToArray(), 0.1f);
}
Run Code Online (Sandbox Code Playgroud)
你需要测试什么是最好的妥协,0.2f仍然可以,0.3f已经透支了很多..
要获得真正好的解决方案,您需要使用DrawBeziers.这将允许您绘制可以通过点的曲线,而无需任何透支,并完全控制曲线的半径; 但要这样你就需要'找',即计算好 control points,这不过是微不足道的......:

这个结果绝不是完美的,但已经足够复杂了.我已经用相同的颜色显示了curve points它们和它们各自control points的颜色.对于每个点,都有一个传入和传出控制点.对于平滑曲线,他们需要在曲线点上具有相同的切线/渐变.
我使用一些辅助函数来计算关于段的一些事情:
main函数计算数组bezier points,即curve points每对之间的左上角和右下角 control points.
如果使用Paint它像这样:
List<PointF> bezz = getBezz(points);
using (Pen pen = new Pen(Color.Black, 2f))
e.Graphics.DrawBeziers(pen, bezz.ToArray());
Run Code Online (Sandbox Code Playgroud)
以下是我使用的功能:
List<float> getGradients(List<PointF> p)
{
List<float> grads = new List<float>();
for (int i = 0; i < p.Count - 1; i++)
{
float dx = p[i + 1].X - p[i].X;
float dy = p[i + 1].Y - p[i].Y;
if (dx == 0) grads.Add(dy == 0 ? 0 : dy > 0 ?
float.PositiveInfinity : float.NegativeInfinity);
else grads.Add(dy / dx);
}
return grads;
}
List<float> getLengths(List<PointF> p)
{
List<float> lengs = new List<float>();
for (int i = 0; i < p.Count - 1; i++)
{
float dx = p[i + 1].X - p[i].X;
float dy = p[i + 1].Y - p[i].Y;
lengs.Add((float)Math.Sqrt(dy * dy + dx * dx));
}
return lengs;
}
List<float> getGaps(List<PointF> p, bool horizontal)
{
List<float> gaps = new List<float>();
for (int i = 0; i < p.Count - 1; i++)
{
float dx = p[i + 1].X - p[i].X;
float dy = p[i + 1].Y - p[i].Y;
gaps.Add(horizontal ? dx : dy);
}
return gaps;
}
List<int> getSigns(List<float> g)
{
return g.Select(x => x > 0 ? 1 : x == 0 ? 0 : -1).ToList();
}
Run Code Online (Sandbox Code Playgroud)
最后是主要功能; 在这里我做了一个区别:极限点(最小值和最大值)的控制点应该与点本身在同一高度.这样可以防止垂直溢出.它们很容易找到:它们的渐变迹象总是会有所改变.
其他点需要对输入和输出控制点具有相同的梯度.我使用段的渐变之间的平均值.(也许一个称重的平均值会更好..)然后我根据段长度来衡量它们的距离.
List<PointF> getBezz(List<PointF> points)
{
List<PointF> bezz = new List<PointF>();
int pMax = points.Count;
List<float> hGaps = getGaps(points, true);
List<float> vGaps = getGaps(points, false);
List<float> grads = getGradients(points);
List<float> lengs = getLengths(points);
List<int> signs = getSigns(grads);
PointF[] bezzA = new PointF[pMax * 3 - 2];
// curve points
for (int i = 0; i < pMax; i++) bezzA[i * 3] = points[i];
// left control points
for (int i = 1; i < pMax; i++)
{
float x = points[i].X - hGaps[i - 1] / 2f;
float y = points[i].Y;
if (i < pMax - 1 && signs[i - 1] == signs[i])
{
float m = (grads[i-1] + grads[i]) / 2f;
y = points[i].Y - hGaps[i-1] / 2f * m * vGaps[i-1] / lengs[i-1];
}
bezzA[i * 3 - 1] = new PointF(x, y);
}
// right control points
for (int i = 0; i < pMax - 1; i++)
{
float x = points[i].X + hGaps[i] / 2f;
float y = points[i].Y;
if (i > 0 && signs[i-1] == signs[i])
{
float m = (grads[i-1] + grads[i]) / 2f;
y = points[i].Y + hGaps[i] / 2f * m * vGaps[i] / lengs[i];
}
bezzA[i * 3 + 1] = new PointF(x, y);
}
return bezzA.ToList();
}
Run Code Online (Sandbox Code Playgroud)
请注意,我没有编码具有相同x坐标的点的情况.所以这对于"功能图"来说是可以的,但不是,比如数字,例如星星......
| 归档时间: |
|
| 查看次数: |
429 次 |
| 最近记录: |