Comment déterminer si un polygone en contient complètement un autre?

9

J'ai 2 polygones. Je connais les coordonnées des sommets des deux polygones. Quelle est la meilleure façon de vérifier si l'un est complètement à l'intérieur de l'autre? Par exemple, l'algorithme ne doit reconnaître que le trapèze noir ci-dessous comme contenu:

exemples de polygones

user960567
la source
Je ne peux pas donner de réponse détaillée pour le moment (je pourrais le faire plus tard), mais vous devriez jeter un œil à une implémentation du théorème de l'axe de séparation pour la détection de collision. L'algorithme peut être légèrement modifié pour vérifier facilement ce que vous voulez. par exemple codezealot.org/archives/55
TravisG
1
Vous ne savez pas exactement ce que vous comprenez d'un polygone à l'intérieur d'un polygone. Que se passe-t-il si tous les points du plus petit polygone sont dans le plus grand, mais que chacun d'eux a un côté sur une même ligne, est-il l'un dans l'autre? i47.tinypic.com/4i0sgg.jpg
speedyGonzales
@speedyGonzales, c'est aussi appelé à l'intérieur.
user960567

Réponses:

5

Il existe des tonnes d'extraits de source pour une méthode qui effectue un test de " point à l'intérieur du polygone ". Le principe provient du théorème de courbe de Jordan pour les polygones ( http://www-cgrl.cs.mcgill.ca/~godfried/teaching/cg-projects/97/Octavian/compgeom.html ).

La manière naïve serait: avoir cette méthode, appelez-la PointInsidePolygon(Point p, Polygon poly):

  bool isInside = true;
  for each (Point p in innerPoly)
  {
    if (!PointInsidePolygon(p, outerPoly))
    {
      isInside = false; // at least one point of the innerPoly is outside the outerPoly
      break;
    }
  }
  if (!isInside) return false;
  // COMPULSORY EDGE INTERSECTION CHECK
  for each (innerEdge in innerPoly)
    for each (outerEdge in outerPoly)
    {
      if (EdgesIntersect(innerEdge, outerEdge))
      {
        isInside = false;
        break;
      }
    }

  return isInside;

Théoriquement, il ne devrait manquer aucun scénario pour vos polygones, mais ce n'est pas la solution optimale.

Remarques sur l'affaire "Edge"

  • PointInsidePolygon(..) doit renvoyer true si le point est sur la bordure du polygone (soit se trouve sur une arête, soit est un sommet)

  • EdgesIntersect(..)doit renvoyer false si le innerEdgeest un sous-ensemble (géométriquement) de outerEdge. Dans ce cas, les bords se coupent évidemment, mais pour les besoins de l'algorithme, nous devons indiquer que l'intersection ne rompt pas la sémantique derrière la isInsidevariable

Général Remakrs :

  • sans vérification de l'intersection arête contre arête, comme indiqué dans les commentaires, l'approche peut renvoyer des faux positifs pour certains polygones concaves (par exemple un quadrilatère en forme de V et un rectangle - le rectangle peut avoir tous ses sommets à l'intérieur de la forme en V, mais l'intersecter). , ayant ainsi au moins quelques zones à l'extérieur).

  • après avoir vérifié qu'au moins l'un des sommets du polygone intérieur se trouve à l'intérieur de celui extérieur, et s'il n'y a pas d'arêtes qui se croisent, cela signifie que la condition recherchée est satisfaite.

teodron
la source
1
Cela retournera des faux positifs lorsque le polygone extérieur est concave.
sam hocevar
2
Assez drôle, alors que les teodron et knight666 ont tort individuellement, lorsqu'ils sont combinés, ils devraient donner une bonne réponse. Si tous les points d'un polygone sont à l'intérieur d'un autre, et si leurs lignes ne se coupent pas, alors le premier poly est complètement à l'intérieur de l'autre.
Hackworth
Certes, il renvoie des faux positifs, il doit également prendre en compte les intersections de bords.
teodron
Cela semble être la bonne réponse. Je pense que je n'ai pas besoin de vérifier l'état de la deuxième boucle.
user960567
Cela ne fonctionne pas pour les intersections d'extrémité ou si les bords se chevauchent.
Brandon Kohn
2

Essayez de faire une intersection de lignes avec chaque ligne rouge. En pseudocode:

// loop over polygons
for (int i = 0; i < m_PolygonCount; i++)
{
    bool contained = false;

    for (int j = 0; j < m_Polygon[i].GetLineCount(); j++)
    {
        for (int k = 0; k < m_PolygonContainer.GetLineCount(); k++)
        {
            // if a line of the container polygon intersects with a line of the polygon
            // we know it's not fully contained
            if (m_PolygonContainer.GetLine(k).Intersects(m_Polygon[i].GetLine(j)))
            {
                contained = false;
                break;
            }
        }

        // it only takes one intersection to invalidate the polygon
        if (!contained) { break; }
    }

    // here contained is true if the polygon is fully inside the container
    // and false if it's not
}

Cependant, comme vous pouvez le voir, cette solution deviendra plus lente lorsque vous ajouterez plus de polygones à vérifier. Une solution différente pourrait être:

  • Rendez le polygone du conteneur à un tampon de pixels de couleur blanche.
  • Rendre un polygone enfant dans un tampon de pixels différent avec une couleur blanche.
  • Masquez le tampon de conteneur sur le tampon de polygone avec un masque XOR.
  • Comptez le nombre de pixels blancs; s'il est supérieur à 0, le polygone enfant n'est pas entièrement contenu par le conteneur.

Cette solution est très rapide, mais cela dépend de votre implémentation (et de ce que vous voulez faire avec le résultat de votre vérification) quelle solution vous convient le mieux.

chevalier666
la source
1
Les intersections de lignes ne suffiront pas à repérer des polygones entièrement contenus.
Kylotan
1
Question: si les polygones sont complètement disjoints, aucune arête ne se coupera. Cela fonctionnera-t-il dans ce cas? La seconde approche basée sur les graphiques devrait fonctionner en effet!
teodron