Point C # dans le polygone

J'essaie de déterminer si un point est à l'intérieur d'un polygone. le polygone est défini par un tableau D'objets ponctuels. Je peux facilement comprendre si le point est à l'intérieur de la boîte délimitée du polygone, mais je ne suis pas sûr de savoir si c'est à l'intérieur du polygone réel ou non. Si possible, je voudrais seulement utiliser C# et WinForms. Je préfère ne pas appeler OpenGL ou quelque chose pour faire cette tâche simple.

Voici le code que j'ai jusqu'à présent:

private void CalculateOuterBounds()
{
    //m_aptVertices is a Point[] which holds the vertices of the polygon.
    // and X/Y min/max are just ints
    Xmin = Xmax = m_aptVertices[0].X;
    Ymin = Ymax = m_aptVertices[0].Y;

    foreach(Point pt in m_aptVertices)
    {
        if(Xmin > pt.X)
            Xmin = pt.X;

        if(Xmax < pt.X)
            Xmax = pt.X;

        if(Ymin > pt.Y)
            Ymin = pt.Y;

        if(Ymax < pt.Y)
            Ymax = pt.Y;
    }
}

public bool Contains(Point pt)
{
    bool bContains = true; //obviously wrong at the moment :)

    if(pt.X < Xmin || pt.X > Xmax || pt.Y < Ymin || pt.Y > Ymax)
        bContains = false;
    else
    {
        //figure out if the point is in the polygon
    }

    return bContains;
}
30
demandé sur Stephen Kennedy 2010-11-22 09:59:21

8 réponses

Voir this c'est en C++ et peut être fait en c# de la même manière.

Pour polygone convexe est trop facile:

Si le polygone est convexe alors on peut considérez le polygone comme un "chemin" de le premier sommet. Un point est sur le intérieur de ce polygones si elle est toujours du même côté de tous les segments de ligne constituant le chemin.

Étant donné un segment de ligne entre P0 (x0, y0) et P1 (x1, y1) , un autre point P (x, y) a la relation suivante pour le segment de ligne. Calculer (y-y0) (x1 - x0) (x - x0) (y1 - y0)

Si elle est inférieure à 0 alors P est à la à droite du segment de ligne, si supérieur que 0 est à gauche, si égale à 0 alors il se trouve sur le segment de ligne.

Voici son code en c#, Je n'ai pas vérifié les cas de bord.

        public static bool IsInPolygon(Point[] poly, Point point)
        {
           var coef = poly.Skip(1).Select((p, i) => 
                                           (point.Y - poly[i].Y)*(p.X - poly[i].X) 
                                         - (point.X - poly[i].X) * (p.Y - poly[i].Y))
                                   .ToList();

            if (coef.Any(p => p == 0))
                return true;

            for (int i = 1; i < coef.Count(); i++)
            {
                if (coef[i] * coef[i - 1] < 0)
                    return false;
            }
            return true;
        }

Je le teste avec un simple rectangle fonctionne bien:

            Point[] pts = new Point[] { new Point { X = 1, Y = 1 }, 
                                        new Point { X = 1, Y = 3 }, 
                                        new Point { X = 3, Y = 3 }, 
                                        new Point { X = 3, Y = 1 } };
            IsInPolygon(pts, new Point { X = 2, Y = 2 }); ==> true
            IsInPolygon(pts, new Point { X = 1, Y = 2 }); ==> true
            IsInPolygon(pts, new Point { X = 0, Y = 2 }); ==> false

Explication sur la requête linq:

poly.Skip(1) ==> crée une nouvelle liste démarrée à partir de la position 1 de la liste poly puis par (point.Y - poly[i].Y)*(p.X - poly[i].X) - (point.X - poly[i].X) * (p.Y - poly[i].Y) Nous allons calculer la direction (qui a mentionné dans le paragraphe référencé). exemple similaire (avec une autre opération):

lst = 2,4,8,12,7,19
lst.Skip(1) ==> 4,8,12,7,19
lst.Skip(1).Select((p,i)=>p-lst[i]) ==> 2,4,4,-5,12
8
répondu Saeed Amiri 2018-08-29 11:29:55

J'ai vérifié les codes ici et tous ont des problèmes.

La meilleure méthode est:

    /// <summary>
    /// Determines if the given point is inside the polygon
    /// </summary>
    /// <param name="polygon">the vertices of polygon</param>
    /// <param name="testPoint">the given point</param>
    /// <returns>true if the point is inside the polygon; otherwise, false</returns>
    public static bool IsPointInPolygon4(PointF[] polygon, PointF testPoint)
    {
        bool result = false;
        int j = polygon.Count() - 1;
        for (int i = 0; i < polygon.Count(); i++)
        {
            if (polygon[i].Y < testPoint.Y && polygon[j].Y >= testPoint.Y || polygon[j].Y < testPoint.Y && polygon[i].Y >= testPoint.Y)
            {
                if (polygon[i].X + (testPoint.Y - polygon[i].Y) / (polygon[j].Y - polygon[i].Y) * (polygon[j].X - polygon[i].X) < testPoint.X)
                {
                    result = !result;
                }
            }
            j = i;
        }
        return result;
    }
44
répondu meowNET 2013-02-21 09:36:21

La réponse acceptée n'a pas fonctionné pour moi dans mon projet. J'ai fini par utiliser le code de ici.

public static bool IsInPolygon(Point[] poly, Point p)
{
    Point p1, p2;
    bool inside = false;

    if (poly.Length < 3)
    {
        return inside;
    }

    var oldPoint = new Point(
        poly[poly.Length - 1].X, poly[poly.Length - 1].Y);

    for (int i = 0; i < poly.Length; i++)
    {
        var newPoint = new Point(poly[i].X, poly[i].Y);

        if (newPoint.X > oldPoint.X)
        {
            p1 = oldPoint;
            p2 = newPoint;
        }
        else
        {
            p1 = newPoint;
            p2 = oldPoint;
        }

        if ((newPoint.X < p.X) == (p.X <= oldPoint.X)
            && (p.Y - (long) p1.Y)*(p2.X - p1.X)
            < (p2.Y - (long) p1.Y)*(p.X - p1.X))
        {
            inside = !inside;
        }

        oldPoint = newPoint;
    }

    return inside;
}
23
répondu Keith 2018-08-15 18:16:08

Vous pouvez utiliser l'algorithme de lancer de rayons. Il est bien décrit dans la page wikipedia pour le point dans le problème de polygone .

C'est aussi simple que de compter le nombre de fois qu'un rayon de l'extérieur à ce point touche les limites du polygone. S'il touche un nombre pair de fois, le point est en dehors du polygone. Si elle touche un nombre impair de fois, le point est à l'intérieur.

Pour compter le nombre de fois que le rayon touche, vous vérifiez les intersections entre le rayon et les côtés du polygone.

6
répondu R. Martinho Fernandes 2010-11-22 07:02:49

Ma réponse est prise à partir d'ici:Lien

J'ai pris le code C et l'ai converti en C# et l'ai fait fonctionner

static bool pnpoly(PointD[] poly, PointD pnt )
    {
        int i, j;
        int nvert = poly.Length;
        bool c = false;
        for (i = 0, j = nvert - 1; i < nvert; j = i++)
        {
            if (((poly[i].Y > pnt.Y) != (poly[j].Y > pnt.Y)) &&
             (pnt.X < (poly[j].X - poly[i].X) * (pnt.Y - poly[i].Y) / (poly[j].Y - poly[i].Y) + poly[i].X))
                c = !c; 
        }
        return c;
    }

Vous pouvez le tester avec cet exemple:

PointD[] pts = new PointD[] { new PointD { X = 1, Y = 1 }, 
                                    new PointD { X = 1, Y = 2 }, 
                                    new PointD { X = 2, Y = 2 }, 
                                    new PointD { X = 2, Y = 3 },
                                    new PointD { X = 3, Y = 3 },
                                    new PointD { X = 3, Y = 1 }};

        List<bool> lst = new List<bool>();
        lst.Add(pnpoly(pts, new PointD { X = 2, Y = 2 }));
        lst.Add(pnpoly(pts, new PointD { X = 2, Y = 1.9 }));
        lst.Add(pnpoly(pts, new PointD { X = 2.5, Y = 2.5 }));
        lst.Add(pnpoly(pts, new PointD { X = 1.5, Y = 2.5 }));
        lst.Add(pnpoly(pts, new PointD { X = 5, Y = 5 }));
3
répondu gil kr 2017-05-23 10:31:34

L'algorithme complet avec le code C est disponible à http://alienryderflex.com/polygon/
Le convertir en C# / winforms serait trivial.

2
répondu basarat 2010-11-22 07:50:56

Je recommande ce merveilleux article de 15 pages par Kai Hormann (Université D'Erlangen) et Alexander Agathos (Université D'Athènes). Il consolide tous les meilleurs algorithmes et vous permettra de détecter les solutions d'enroulement et de coulée de rayons.

Le problème du point dans le polygone pour les polygones arbitraires

L'algorithme est intéressant à mettre en œuvre, et en vaut la peine. Cependant, il est si complexe qu'il est inutile pour moi de toute partie directement. Je vais plutôt rester avec dire que si vous voulez l'algorithme le plus efficace et polyvalent, je suis certain que c'est tout.

L'algorithme devient complexe car il est très hautement optimisé, il faudra donc beaucoup de lecture pour comprendre et implémenter. Cependant, il combine les avantages des algorithmes de distribution de rayons et de nombre d'enroulement et le résultat est un nombre unique qui fournit les deux réponses à la fois. Si le résultat est supérieur à zéro et de l'impair, alors le point est complètement, mais si le résultat est un nombre pair, alors le point est contenu dans une section du polygone qui se replie sur lui-même.

Bonne chance.

1
répondu Owen Godfrey 2016-05-12 07:06:49

MeowNET anwser n'inclut pas les sommets des polygones dans le polygone et pointe exactement sur les bords horizontaux. Si vous avez besoin d'un algorithme "inclusif" exact:

public static bool IsInPolygon(this Point point, IEnumerable<Point> polygon)
{
    bool result = false;
    var a = polygon.Last();
    foreach (var b in polygon)
    {
        if ((b.X == point.X) && (b.Y == point.Y))
            return true;

        if ((b.Y == a.Y) && (point.Y == a.Y) && (a.X <= point.X) && (point.X <= b.X))
            return true;

        if ((b.Y < point.Y) && (a.Y >= point.Y) || (a.Y < point.Y) && (b.Y >= point.Y))
        {
            if (b.X + (point.Y - b.Y) / (a.Y - b.Y) * (a.X - b.X) <= point.X)
                result = !result;
        }
        a = b;
    }
    return result;
}
0
répondu Dubbs777 2018-03-22 17:13:10