web-dev-qa-db-fra.com

Comment déterminer si un point est à l'intérieur d'un polygone convexe 2D?

J'ai un polygone convexe (typiquement juste un carré tourné) et je connais tous les 4 points. Comment déterminer si un point donné (jaune/vert) est à l'intérieur du polygone?

enter image description here

EDIT: Pour ce projet particulier, je n’ai pas accès à toutes les bibliothèques du JDK, telles que AWT.

20
NPike

Cette page: http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html montre comment procéder pour tout polygone.

J'ai une implémentation Java de ceci, mais c'est trop gros pour poster ici dans son intégralité. Cependant, vous devriez être capable de le résoudre:

class Boundary {
    private final Point[] points; // Points making up the boundary
    ...


    /**
     * Return true if the given point is contained inside the boundary.
     * See: http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
     * @param test The point to check
     * @return true if the point is inside the boundary, false otherwise
     *
     */
    public boolean contains(Point test) {
      int i;
      int j;
      boolean result = false;
      for (i = 0, j = points.length - 1; i < points.length; j = i++) {
        if ((points[i].y > test.y) != (points[j].y > test.y) &&
            (test.x < (points[j].x - points[i].x) * (test.y - points[i].y) / (points[j].y-points[i].y) + points[i].x)) {
          result = !result;
         }
      }
      return result;
    }
}

Et voici un croquis de la classe Point

/**
 * Two dimensional cartesian point.
 */
public class Point {
  public final double x;
  public final double y;
  ...
}
69
Dean Povey

Pour ceux qui voudraient comprendre comment fonctionne la méthode écrite par Dean Povey ci-dessus, voici l'explication:

La méthode examine un "rayon" qui commence au point testé et s'étend à l'infini jusqu'au côté droit de l'axe X. Pour chaque segment de polygone, il vérifie si le rayon le traverse. Si le nombre total de segments croisés est impair, le point testé est considéré à l'intérieur du polygone, sinon - il est en dehors de .

Pour comprendre le calcul du franchissement, considérons la figure suivante:

            v2
            o
           /
          / c (intersection)
o--------x----------------------> to infinity
t       /
       /   
      /
     o
     v1

Pour que l'intersection se produise, test.y doit être compris entre les valeurs y des sommets du segment (v1 et v2). C'est la première condition de l'instruction if dans la méthode. Si tel est le cas, la ligne horizontale doit intersecter le segment. Il ne reste plus qu’à établir si l’intersection se situe à la droite du point testé ou à sa gauche. Cela nécessite de trouver la coordonnée x du point d'intersection, qui est:

              t.y - v1.y
c.x = v1.x + ----------- * (v2.x - v1.x)
             v2.y - v1.y

Il ne reste plus qu'à examiner les subtilités:

  • Si v1.y == v2.y, le rayon suit le segment et Par conséquent, le segment n'a aucune influence sur le résultat. En effet, la première partie De l'instruction if renvoie false dans ce cas .
  • Le code se multiplie d'abord et ensuite seulement se divise. Ceci est fait pour supporter De très petites différences entre les versions v1.x et v2.x, ce qui pourrait conduire à un zéro après la division, en raison de l'arrondissement .
  • Enfin, le problème du croisement exactement sur un sommet devrait être résolu. Considérons les deux cas suivants:
         o                    o
         |                     \     o
         | A1                C1 \   /
         |                       \ / C2  
o--------x-----------x------------x--------> to infinity
        /           / \
    A2 /        B1 /   \ B2
      /           /     \ 
     o           /       o
                o

Maintenant, pour vérifier si cela fonctionne, vérifiez vous-même ce qui est retourné pour chaque Des 4 segments par la condition if dans le corps de la méthode . Vous devriez trouver que les segments situés au-dessus du rayon (A1, C1, C2 ) reçoivent un résultat positif, tandis que ceux situés en dessous (A2, B1, B2) reçoivent un résultat négatif. Cela signifie que le sommet A contribue un nombre impair (1) au nombre de croisements , Tandis que B et C contribuent à un nombre pair (0 et 2, respectivement), ce qui est exactement ce que vous souhaitez. A est en effet un véritable croisement du polygone, alors que B .__ et C ne sont que deux cas de "survol".

30
Nadav

En supposant que votre point se trouve à la coordonnée y, calculez simplement les positions x où chaque Des lignes du polygone (non horizontales) se croisent y. Comptez le nombre de x positions qui sont Inférieures à la position x de votre point. Si le nombre de x positions est impair, votre point est À l'intérieur du polygone. Remarque: cela fonctionne pour tous les polygones, pas seulement convexes. Pensez-y de la manière suivante: Tracez une ligne d’infiniment loin directement vers votre point. Lorsque cette ligne traverse une ligne de polygone , Elle se trouve maintenant à l'intérieur du polygone. Traverser à nouveau la ligne, à l'extérieur. Traverser à nouveau, À l'intérieur (et ainsi de suite). J'espère que cela t'aides!

17
Jim

La classe Java.awt.Polygon a un certain nombre de méthodes contains(...) si vous utilisez Polygon objects pour représenter votre polygone.

9
Kavka

Juste pour ajouter une implémentation (simple) Java du code original en C depuis code suggéré par @Dean Povey (je ne sais pas pourquoi @Dean Povey fait référence à une implémentation volumineuse):

static boolean pnpoly(double[] vertx, double[] verty, double testx, double testy)
{
    int nvert = vertx.length;
    int i, j;
    boolean c = false;
    for (i = 0, j = nvert-1; i < nvert; j = i++) {
        if ( ((verty[i]>testy) != (verty[j]>testy)) &&
                (testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
            c = !c;
    }
    return c;
}   

Je n'ai pas modifié le cas pour se conformer à la règle Java afin d'afficher les modifications minimales requises. Je l'ai également testé dans des cas simples et cela fonctionne bien.

4
Eypros

Disons que x [] est le tableau de x points et y [] est le tableau de y points.
Vous êtes censé renvoyer 1 si le point existe dans le polygone et 2 sinon. où (planeX, planeY) est le point à vérifier.

//check like this
return new Polygon(x,y,x.length).contains(planeX, planeY)?1:2;
1
Apoorva Ambhoj

Vérifiez si elle se trouve du même côté que les 4 demi-plans définis par les lignes contenant les segments composant les côtés du quad.

Ici est une bonne explication.

1
user1118321

Abscissas de polygones x_array: Array[Integer]

Ordonnées du polygone: y_array: Array[Integer]

Point: x: Integer, y: Integer

import Java.awt.Polygon
import Java.awt.Point
...

final boolean isInPolygon = 
    new Polygon(x_array,y_array,x_array.length).contains(new Point(x, y));

Dans cet exemple, nous créons un objet Java.awt.Polygon et utilisons la méthode contient pour vérifier si vos coordonnées se trouvent dans la forme que vous avez conçue.

J'utilise l'objet Java.awt.Point pour représenter les coordonnées afin de rendre le code élégant mais c'est optionnel, vous pouvez directement utiliser .contains(x, y)

0
Seraf