在我的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%的区域。
如果有人以正确的方式把我放在一边会很好,我应该在原始曲线周围找到一个漂亮的信封?
更新 因为我是如此的诺布,我无法找到一种方法来实现这个问题的答案直到现在,所以请一个赏金,看看somone是否可以告诉我至少这个问题的编码方法。
答案 0 :(得分:3)
您可以尝试在每对点之间找到渐变,并计算通过中点的正交两侧的两个点。
然后,您可以将另外两行定义为可用于绘制信封的一组点。
答案 1 :(得分:1)
这一切都取决于您希望信封的大小。
您可以通过计算下一个点的斜率和前一个点的斜率来计算/估计每个点的曲线斜率,平均这些斜率,然后计算斜率的垂直向量。
将此向量添加到曲线的点;这为你提供了信封的右手边缘。
从曲线点减去此向量;这会给你信封的左边缘。
如果点太远或点出现非常突然的变化,此方法将失败。
答案 2 :(得分:1)
最好的办法是迭代你的点数组并计算每次连续两个点的垂直向量(参见Calculating a 2D Vector's Cross Product的实现线索)。沿着这些垂直向量在任一方向上投影,以生成信封的两个点阵列。
这个函数大致使用段中点生成它们(只要点数很高而你的偏移量不是太小,绘制时应该看起来不错):
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));
}
}
答案 3 :(得分:0)
这可能是一个愚蠢的建议。或许不是自己画信封,也许你可以让winforms为你做。尝试使用宽度较大的笔将信封绘制成一条线。也许它可能会奏效。
如果你看一下改变笔宽的msdn例子,你可能会看到我的意思。