10

在我的 C# WinForms 应用程序中,我有一个包含 2 条曲线的图片框(由电压/电流测量结果)。X轴是电压,Y轴是电流。电压轴的范围是 -5 到 5,但电流轴的范围要小得多,范围是 -10 uA 到 10 uA。任务是查看第二条曲线是否在第一条曲线的 10% 以内。

对于目视检查,我试图在第一条曲线(蓝色曲线)周围画一个信封。曲线只是一个PointF数组。目前,由于我不知道如何在蓝色曲线周围绘制正确的包络线,所以我只绘制了另外两条曲线,它们是实际曲线的 X 点加减原始曲线的 10% 的结果。当然,这是一种不好的方法,但至少对于明显垂直的曲线部分,它是有效的。但是一旦曲线在其非垂直部分,这个技巧就不再起作用了,如下图所示:

在此处输入图像描述

这是我用来绘制信封的代码:

public Bitmap DrawEnvelope(double[,] pinData, float vLimit, float iLimit)
{
    g = Graphics.FromImage(box);
    g.SmoothingMode = SmoothingMode.AntiAlias;
    g.PixelOffsetMode = PixelOffsetMode.HighQuality;

    PointF[] u = new PointF[pinData.GetLength(0)]; //Up line
    PointF[] d = new PointF[pinData.GetLength(0)]; //Down Line
    List<PointF> joinedCurves = new List<PointF>();

    float posX = xMaxValue * (vLimit / 100);
    float minX = posX * -1;


    for (int i = 0; i < pinData.GetLength(0); i++)
    {
        u[i] = new PointF(400 * (1 + (((float)pinData[i, 0]) + minX) / (xMaxValue + vExpand)), 400 * (1 - ((float)pinData[i, 1] * GetInvers((yMaxValue + iExpand)))));
    }

    for (int i = 0; i < pinData.GetLength(0); i++)
    {
        d[i] = new PointF(400 * (1 + (((float)pinData[i, 0]) + posX) / (xMaxValue + vExpand)), 400 * (1 - ((float)pinData[i, 1] * GetInvers((yMaxValue + iExpand)))));
    }


    Pen pengraph = new Pen(Color.FromArgb(50, 0 ,0 ,200), 1F);
    pengraph.Alignment = PenAlignment.Center;

    joinedCurves.AddRange(u);
    joinedCurves.AddRange(d.Reverse());

    PointF[] fillPoints = joinedCurves.ToArray();
    SolidBrush fillBrush = new SolidBrush(Color.FromArgb(40, 0, 0, 250));
    FillMode newFillMode = FillMode.Alternate;

    g.FillClosedCurve(fillBrush, fillPoints, newFillMode, 0);

    g.Dispose();
    return box;
}

绿色圆圈是我自己添加的,它们表示第二条曲线(红色曲线)可能与原始曲线有大于 10% 的差异的区域。

如果有人把我放在正确的方式会很好,我应该怎么看才能在原始曲线周围获得一个漂亮的包络?

更新 因为我太菜鸟了,直到现在我都找不到一种方法来实现对这个问题的答案,所以请赏金看看是否有人可以向我展示至少一种解决这个问题的编码方法。

4

4 回答 4

3

您可以尝试找到每对点之间的梯度,并计算通过中点的正交上任一侧的两个点。

然后,您将再定义两条线作为一组点,您可以使用它们来绘制信封。

于 2012-06-05T11:32:36.870 回答
1

这完全取决于您希望信封大小的方式。

您可以通过计算到下一个点的斜率和到前一个点的斜率来计算/猜测每个点的曲线斜率,平均这些,然后计算斜率的垂直向量。

将此向量添加到曲线的点;这为您提供了信封的右侧边缘。

从曲线的点减去这个向量;这为您提供了信封的左侧边缘。

如果点相距太远或点出现非常突然的变化,此方法将失败。

于 2012-06-05T12:30:19.133 回答
1

您最好的选择是迭代您的点数组并每次计算与两个连续点的垂直向量(有关实现线索,请参阅计算二维向量的叉积)。沿这些垂直向量向任一方向投影,以生成包络的两个点阵列。

此函数使用段中点粗略地生成它们(只要点数很高并且您的偏移量不太小,绘制时应该看起来不错):

private void GetEnvelope(PointF[] curve, out PointF[] left, out PointF[] right, float offset)
        {
            left = new PointF[curve.Length - 1];
            right = new PointF[curve.Length - 1];

            for (int i = 1; i < curve.Length; i++)
            {
                PointF normal = new PointF(curve[i].Y - curve[i - 1].Y, curve[i - 1].X - curve[i].X);
                float length = (float)Math.Sqrt(normal.X * normal.X + normal.Y * normal.Y);
                normal.X /= length;
                normal.Y /= length;

                PointF midpoint = new PointF((curve[i - 1].X + curve[i].X) / 2F, (curve[i - 1].Y + curve[i].Y) / 2F);
                left[i - 1] = new PointF(midpoint.X - (normal.X * offset), midpoint.Y - (normal.Y * offset));
                right[i - 1] = new PointF(midpoint.X + (normal.X * offset), midpoint.Y + (normal.Y * offset));
            }
        }
于 2012-06-08T13:33:48.790 回答
1

这可能是一个愚蠢的建议。也许不是自己画信封,也许你可以让 winforms 为你做。尝试用宽度较大的笔将信封画成一条线。也许它可能会奏效。

如果您查看这个有关改变笔宽的 msdn 示例,您可能会明白我的意思。

http://msdn.microsoft.com/en-us/library/3bssbs7z.aspx

于 2012-06-12T05:11:38.523 回答