4

を時計回りに巻くか反時計回りに巻くGraphicsPathかを決定するオブジェクトの拡張メソッドを作成する必要があります。GraphicsPath

このようなもの:

    public static bool IsClockwise(GraphicsPath gp)
    {
        bool bClockWise = false;
        PointF[] pts = gp.PathPoints;

        foreach (PointF pt in pts)
        {
            //??
        }

        return bClockWise;
    }

foreach注:ここでのポイントが必要だと思いますGraphicsPathが、もっと良い方法があれば、遠慮なく提供してください。

この理由は、隣接するGraphicsPathの重複するセクションが「埋められる」()または「穴」( )FillModeであるかどうかを決定するためです。ただし、これは、隣接するGraphicsPathが同じ方向に巻かれている場合にのみ当てはまります。GraphicsPathWindingAlternate

フィルモードワインディングvsオルタネート

2つのパスが反対方向に巻かれている場合は、に設定してもFillModeWinding不要な)穴ができます。

興味深いことに、GraphicsPath.Reverse()にはパスの方向を変更できるメソッドがあります(これで問題が解決します)が、パスの方向を知らずにGraphicsPathをいつ反転する必要があるかを知ることはできません。

この拡張方法を手伝ってもらえますか?

4

3 に答える 3

4

この質問が私の興味を引いたので、私はほとんどこれをそこに投げ出しています.それは私が専門知識を持っていないものでした.

Point in Polygon 疑似コードを使用して、状況に合うようにしました。何かが時計回りに巻かれているか反時計回りに巻かれているかを判断することのみに関心があるようです。これは簡単なテストと非常に単純な (大雑把な) C# 実装です。

[Test]
public void Test_DetermineWindingDirection()
{

    GraphicsPath path = new GraphicsPath();

    // Set up points collection
    PointF[] pts = new[] {new PointF(10, 60),
                    new PointF(50, 110),
                    new PointF(90, 60)};
    path.AddLines(pts);

    foreach(var point in path.PathPoints)
    {
        Console.WriteLine("X: {0}, Y: {1}",point.X, point.Y);
    }

    WindingDirection windingVal = DetermineWindingDirection(path.PathPoints);
    Console.WriteLine("Winding value: {0}", windingVal);
    Assert.AreEqual(WindingDirection.Clockwise, windingVal);

    path.Reverse();
    foreach(var point in path.PathPoints)
    {
        Console.WriteLine("X: {0}, Y: {1}",point.X, point.Y);
    }

    windingVal = DetermineWindingDirection(path.PathPoints);
    Console.WriteLine("Winding value: {0}", windingVal);
    Assert.AreEqual(WindingDirection.CounterClockWise, windingVal);
}

public enum WindingDirection
{
    Clockwise,
    CounterClockWise
}

public static WindingDirection DetermineWindingDirection(PointF[] polygon)
{
    // find a point in the middle
    float middleX = polygon.Average(p => p.X);
    float middleY = polygon.Average(p => p.Y);
    var pointInPolygon = new PointF(middleX, middleY);
    Console.WriteLine("MiddlePoint = {0}", pointInPolygon);

    double w = 0;
    var points = polygon.Select(point =>
                                    { 
                                        var newPoint = new PointF(point.X - pointInPolygon.X, point.Y - pointInPolygon.Y);
                                        Console.WriteLine("New Point: {0}", newPoint);
                                        return newPoint;
                                    }).ToList();

    for (int i = 0; i < points.Count; i++)
    {
        var secondPoint = i + 1 == points.Count ? 0 : i + 1;
        double X = points[i].X;
        double Xp1 = points[secondPoint].X;
        double Y = points[i].Y;
        double Yp1 = points[secondPoint].Y;

        if (Y * Yp1 < 0)
        {
            double r = X + ((Y * (Xp1 - X)) / (Y - Yp1));
            if (r > 0)
                if (Y < 0)
                    w = w + 1;
                else
                    w = w - 1;
        }
        else if ((Y == 0) && (X > 0))
        {
            if (Yp1 > 0)
                w = w + .5;
            else
                w = w - .5;
        }
        else if ((Yp1 == 0) && (Xp1 > 0))
        {
            if (Y < 0)
                w = w + .5;
            else
                w = w - .5;
        }
    }
    return w > 0 ? WindingDirection.ClockWise : WindingDirection.CounterClockwise;
}

私が入れた違いは、これをあなたの問題に少し固有のものにし、すべてのポイントから X と Y の平均値を計算し、それを「ポリゴン内のポイント」値として使用することです。したがって、ポイントの配列だけを渡すと、それらの中間にあるものが見つかります。

また、 V i+1 がポイント配列の境界に達したときにラップアラウンドする必要があるという仮定を立てたので、

if (i + 1 == points.Count)
    // use points[0] instead

これは最適化されていません。質問に答える可能性のあるものです。そして、おそらくあなたはすでにこれを自分で思いついています.

ここでは、建設的な批判を期待しています。:)

于 2010-11-17T23:22:35.547 に答える
2

いいえ、ポイントをループして巻き順を決定する必要があります。ウェブ上にはいくつかのアルゴリズムがあります。

http://www.engr.colostate.edu/~dga/dga/papers/point_in_polygon.pdf

それを実装する必要があったときは、地球の表面 (地理空間アプリ) 用に修正された、The Graphics Gems の書籍のアルゴリズムを使用したと思います。頭のてっぺんから、コンポーネントベクトルを乗算して合計したと思います。合計の符号は巻き順を与えました。

于 2010-11-17T20:34:46.117 に答える
0

編集:

以下はすべて無視してください。問題が見つかりました。

上記のコードの最後の行は、次のように変更する必要があります。

return w > 0 ? WindingDirection.CounterClockWise : WindingDirection.Clockwise;

return w > 0 ? WindingDirection.ClockWise : WindingDirection.CounterClockwise;

それ以外の場合、コードはうまく機能し、これを答えとして受け入れました(ただし、アルゴリズムを使用して記事を見つけた @winwaed に小道具を提供したいと思います)。

---ignore--- (歴史的な目的のみ)

こんにちはデイブ、

何が起こっているのか (まだ) わかりませんが、反時計回りのアウトラインの関数から「時計回り」という誤った応答が返されます。

Inkscape 0.48 で表示されたアウトラインの 1 つを次に示します。これには、パスの方向を小さな半矢印の頭で表示する (新しい) オプションがあります。ご覧のとおり、外側のパスは反時計回りに曲がります (関数は時計回りに返されますが)。

反時計回り巻き

早速調べてみます…

于 2010-11-18T03:43:26.260 に答える