10

私のC#WinFormsアプリケーションには、2つの曲線をホストするピクチャーボックスがあります(電圧/電流測定の結果)。X軸は電圧、Y軸は電流です。電圧軸は-5から5の範囲ですが、電流軸は-10uAから10uAの範囲のはるかに小さいスケールです。タスクは、2番目の曲線が最初の曲線の10%以内にあるかどうかを確認することです。

目視検査のために、最初の曲線(青い曲線)の周りに封筒を描画しようとしています。曲線は単なるPointF配列です。現時点では、青い曲線の周りに正しいエンベロープを描画する方法がわからないため、実際の曲線のXポイントを元の曲線の10%で減算した結果、他の2つの曲線を描画します。もちろん、これは悪いアプローチですが、少なくとも曲線の特に垂直な部分では機能します。ただし、下の図に示すように、曲線が非垂直セクションにあるとすぐに、このトリックは機能しなくなります。

ここに画像の説明を入力してください

封筒を描くために使用しているコードは次のとおりです。

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

緑の円は自分で追加したもので、2番目の曲線(赤の曲線)が元の曲線との差が10%を超える可能性がある領域を示しています。

誰かが私を正しい方向に導いてくれたらいいのにと思いますが、元のカーブの周りに素敵なエンベロープを実現するために何を探すべきですか?

更新 私はとても初心者なので、これまでこの質問に与えられた答えを実装する方法を見つけることができません。それで、誰かがこの問題へのコーディングアプローチを少なくとも私に親切に見せてくれるかどうかを確認するために賞金を払ってください。

4

4 に答える 4

3

ポイントの各ペア間の勾配を見つけて、中点を通る直交線上にある両側の 2 つのポイントを計算してみることができます。

次に、エンベロープの描画に使用できる点のセットとして定義された 2 本の線を追加します。

于 2012-06-05T11:32:36.870 に答える
1

それはすべて、エンベロープのサイズを設定する方法によって異なります。

次のポイントへの勾配と前のポイントへの勾配を計算し、これらを平均してから、勾配に垂直なベクトルを計算することにより、各ポイントの曲線の勾配を計算/推定できます。

このベクトルを曲線の点に追加します。これにより、エンベロープの右端が得られます。

曲線の点からこのベクトルを引きます。これにより、エンベロープの左端が得られます。

ポイントが離れすぎているか、ポイントに非常に突然の変化が見られる場合、この方法は失敗します。

于 2012-06-05T12:30:19.133 に答える
1

最善の策は、ポイント配列を反復し、毎回2つの連続するポイントへの垂直ベクトルを計算することです(実装の手がかりについては、2Dベクトルの外積の計算を参照してください)。これらの垂直ベクトルに沿っていずれかの方向に投影して、エンベロープの2点配列を生成します。

この関数は、セグメントの中点を使用してそれらを大まかに生成します(ポイント数が多く、オフセットが小さすぎない限り、プロットすると問題ないように見えます)。

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 に答える