GDI +曲线"四溢" [英] GDI+ curve "overflowing"

查看:166
本文介绍了GDI +曲线"四溢"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前使用GDI +绘制线图,并使用 Graphics.DrawCurve 来理顺线。问题是,该曲线不总是匹配我给它的点,这使得曲线生长出在某些点图形帧的,如下所示(红色是 Graphics.DrawLines ,绿色是 Graphics.DrawCurve

I'm currently using GDI+ to draw a line graph, and using Graphics.DrawCurve to smooth out the line. The problem is that the curve doesn't always match the points I feed it, and that makes the curve grow out of the graph frame in some points, as seen below(red is Graphics.DrawLines, green is Graphics.DrawCurve).

我将如何去解决呢?

推荐答案

最简单的办法是设置一个张力:

The simplest solution is to set a tension:

绿色曲线绘制默认紧张,蓝某设定张力

The green curve is drawn with the default tension, the blue one set a tension of 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);

}

您将需要测试什么是最好的妥协, 0.2F 仍然是确定, 0.3f 已经透支了不少。

You will need to test what is the best compromise, 0.2f is still ok, 0.3f is already overdrawing quite a bit..

有关一个很好的解决方案,您将需要使用的 DrawBeziers 。这将让你绘制曲线可以通过没有任何透支和曲线半径的全面控制点;但对这样你就需要发现,即计算的 控制点,这是什么,但平凡..:

For a really good solution you will need to use DrawBeziers. This will let you draw curves that can go through the points without any overdrawing and with full control of the radius of the curves; but to to so you will need to 'find', i.e. calculate good control points, which is anything but trivial..:

这结果绝不是完美的,但已经够复杂了。我已经显示曲线点及其各自的控制点在相同的颜色。每个点有一个接收发送控制点。对于一个平滑的曲线,他们需要有自己的曲线点相同的切线/梯度

This result is by no means perfect but already complicated enough.. I have displayed the curve points and their respective control points in the same color. For each point there is an incoming and an outgoing control point. For a smooth curve they need to have the same tangents/gradients in their curve points.

我使用了一些辅助函数来计算对段的几件事情:

I use a few helper functions to calculate a few things about the segments:


  • 渐变

  • 梯度的迹象名单
  • 段长度
  • 列表
  • 的水平和点之间的垂直间距

  • 名单
  • A list of gradients
  • A list of signs of the gradients
  • A list of segment lengths
  • Lists of horizontal and of vertical gaps between points

主函数计算的贝塞尔点阵列,即曲线点和每一对之间的先前的左的和的下一个右控制点

The main function calculates the array of bezier points, that is the curve points and between each pair the previous left and the next right control points.

油漆事件是这样使用:

List<PointF> bezz = getBezz(points);

using (Pen pen = new Pen(Color.Black, 2f))
       e.Graphics.DrawBeziers(pen, bezz.ToArray());

下面是我使用的功能:

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();  
}

和最后的主要功能;在这里我做一个区别:极端点(最低和放大器;最大值)应该有相同的高度点自己的控制点。这将防止垂直四溢。他们很容易找到。他们的梯度永远altenate

And finally the main function; here I make a distinction: Extreme points ( minima & maxima) should have their control points on the same height as the points themselves. This will prevent vertical overflowing. They are easy to find: The signs of their gradients will always altenate.

其他景点的迹象需要有传入和outcoming控制点相同的梯度。我用的是段梯度之间的平均水平。 (也许加权平均会更好。)和我根据线段长度..

Other points need to have the same gradient for incoming and outcoming control points. I use the average between the segments' gradients. (Maybe a weighed average would be better..) And I weigh their distance according to the segment lengths..

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();
}

请注意,我没有代码点具有相同x的情况下-坐标。因此,这是确定'功能图',但不是,说的数字,例如像明星..

Note that I didn't code for the case of points with the same x-coordinate. So this is ok for 'functional graphs' but not for, say figures, like e.g. stars..

这篇关于GDI +曲线&QUOT;四溢&QUOT;的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆