11

In my C# WinForms application I have a picturebox that hosts 2 curves (Resulted from a voltage/current measurement). The X axis is voltage and Y axis is current. The voltage axis is ranged from -5 to 5 but the current axis is a much smaller scale ranged from -10 uA to 10 uA. The task is to see if the second curve is within 10% of the first curve.

For visual inspection I am trying to draw an envelope around the first curve (Blue one). The curve is just a PointF array. At the moment since I have no idea how to draw a correct envelope around the blue curve, I just draw two other curves that are result of X points of the actual curve added and subtracted by 10% of the original curve. Of course this is a bad approach, but atleast for the section of the curve that is noticably vertical, it works. But as soon as the curve is on its non vertical section, this trick does not work anymore, as you can see in the picture below:

enter image description here

Here is the code that I am using to draw the envelope:

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

The green circles are added by myself, and they indicate the region that the second curve (Red one) is potentially has a difference bigger than 10% from the orginal curve.

Would be nice if someone put me in the right way, what should I look to to achive a nice envelope around original curve?

UPDATE Because I am so noob I cant find a way to implement the answers given to this question until now, So put a bounty to see if somone can kindly show me atleast a coding approach to this problem.

Dumbo
  • 13,555
  • 54
  • 184
  • 288
  • 2
    Is the envelope meant to be the +- 10% region? If so, then I think the first thing you need is more definiteness about what's actually meant by "within 10%". Current within 10% at each voltage? Voltage within 10% at each current? Some other thing? If this comes from a school/university assignment, what was the actual wording? If it's something you're doing for business reasons, what are the underlying requirements? Once that's clear, it will probably be much clearer what your "envelope" should look like. – Gareth McCaughan Jun 05 '12 at 11:35
  • 2
    Can you not just plot your original curve again, make the line thickness very large and the opacity low? – Dan Jun 05 '12 at 11:36
  • @Dan I have already tried to make the orginal curve with a bigger pen in size, but it is a bad idea the result would be worse than what I am doing now. – Dumbo Jun 05 '12 at 11:38
  • @Dan Also I cant find a relation between line thickness and actual percentage of the original curve. – Dumbo Jun 05 '12 at 11:39
  • @GarethMcCaughan By 10% I do not know myself what does it mean! My best guess would be both voltage and current points added by 10 percent to make the upper line and also 10% for the lower line... – Dumbo Jun 06 '12 at 22:19
  • 1
    If the 10% requirement comes from (say) a homework assignment, then you should (1) tell us exactly what it said, and (2) add the "homework" tag to the question. If the problem statement is really unclear, you might consider contacting the person who set it and asking for clarification. – Gareth McCaughan Jun 06 '12 at 23:37
  • 1
    (And if it comes from a real-world problem, then let's hear more about the real-world problem it comes from; perhaps that will make it more obvious what the right criterion is.) – Gareth McCaughan Jun 06 '12 at 23:38
  • @GarethMcCaughan Sorry to get to you very late...yes it was coming from a real world problem. As I said this was a V/I Curve of a given diode. The task was to see after stress testing the diode, its V/I graph changes or not. if the change was more than 10% of the graph prior to stress test, then the diode should have been marked as failed! – Dumbo Jun 24 '15 at 03:34

5 Answers5

3

You could try finding the gradient between each pair of points and calculating two points either side that are on the orthogonal that passes through the midpoint.

You would then have two more lines defined as a set of points that you could use to draw the envelope.

Nick Butler
  • 24,045
  • 4
  • 49
  • 70
1

It all depends on the way you want the envelop to be sized.

You could calculate/guestimate the slope of the curve in each point by calculating the slope to the next point and the slope to the previous point, average these and then calculate a perpendicular vector to the slope.

Add this vector to the point of the curve; this gives you the right-hand edge of the envelop.

Subtract this vector from the point of the curve; this gives you the left-hand edge of the envelop.

This method will fail if the points are too far apart or very sudden changes in the points appear.

Emond
  • 50,210
  • 11
  • 84
  • 115
  • Hmmm... I just noticed that Nicholas thought of the same. – Emond Jun 05 '12 at 12:34
  • @Thanks for the tip...any chance to see how should the code look like? atleast for some of the data points? – Dumbo Jun 05 '12 at 16:07
  • I need to know how you want the envelop to be drawn. – Emond Jun 09 '12 at 17:31
  • I think you mean what can be the values of the envelope, if I am right! What I mean is the original curve should be sourunded by some indication to find out if the result curve(red one) is within a percentage of it. What I think is calculating (for example 10%) two other curves with n% bigger and less values to form a kind of bound. But the problem would be if the first pair of point get increaed by n% then the bound starts at a different location. Please let me know if I was clear :P – Dumbo Jun 09 '12 at 20:40
  • A percentage of what? the x-value, the y-value? What are you trying to show? What does the envelop mean? – Emond Jun 10 '12 at 05:58
1

Your best bet is to iterate your point array and to calculate a perpendicular vector to two consecutive points each time (see Calculating a 2D Vector's Cross Product for implementation clues). Project in either direction along these perpendicular vectors to generate the two point arrays of your envelope.

This function generates them roughly using segment midpoints (as long as the point count is high and your offset is not too small it should look ok when plotted):

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));
            }
        }
Community
  • 1
  • 1
zeFrenchy
  • 6,541
  • 1
  • 27
  • 36
1

This is probably a dumb suggestion. Perhaps instead of drawing the envelope yourself, maybe you could let winforms do it for you. Try drawing the envelope as a line with a pen that has a larger width. Perhaps it might work.

If you look at this msdn example on varying the pen width, you might see what I mean.

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

0

2 (probably incorrect) possibilities.

  1. Do what you did originally to get the pale blue wide area, but also do it in the vertical direction (not just the horizontal)

  2. Do what Dan suggested with a REALLY thick line (in pale blue) then draw it again, then draw the original (thin) line on top of it.