Vérifier qu'un point est dans un polygone

Réponses à toutes vos questions après le Bac (Fac, Prépa, etc.)
Avatar de l’utilisateur
ampholyte
Membre Transcendant
Messages: 3940
Enregistré le: 21 Juil 2012, 08:03

Vérifier qu'un point est dans un polygone

par ampholyte » 21 Juil 2012, 08:16

Bonjour,

Je travaille actuellement sur un script 3D et je rencontre un petit soucis.

Voilà je possède un tableau de point contenant un polygone quelconque.
Je veux réussir à savoir si un point créé aléatoirement est contenu dans ce polygone.

On se ramènera à un problème 2D (on ne prendra pas en compte l'axe Y).

Je pensais travailler avec des équations de plan (ce qui aurait été assez simple à trouver) mais pour le coup je sèche un peu sur la méthode et les équations à poser pour vérifier que le point se trouve (ou non) dans mon polygone.

Quelqu'un aurait-il une idée sur la méthode mathématique à utiliser pour mon problème ?

Voici une petite image pour illustrer mon problème :lol3:

Image

Merci d'avance pour votre aide :we:,

Ampholyte



Skullkid
Habitué(e)
Messages: 3075
Enregistré le: 08 Aoû 2007, 20:08

par Skullkid » 21 Juil 2012, 09:06

Bonjour, l'algorithme le plus classique (pas forcément le plus efficace) à ma connaissance c'est de compter les intersections entre le polygone/polyèdre et une demi-droite qui part du point considéré. S'il y a un nombre pair d'intersections, le point est à l'extérieur, sinon il est à l'intérieur. Pour que ça marche correctement il ne faut pas compter les cas où au moins une des intersections est un sommet du polygone/polyèdre.

Avatar de l’utilisateur
ampholyte
Membre Transcendant
Messages: 3940
Enregistré le: 21 Juil 2012, 08:03

par ampholyte » 21 Juil 2012, 10:43

Merci pour cette réponse,

Est-ce que cette algorithme est toujours vérifiée ou existe-t-il des cas particuliers ?

Existe-t-il également un algorithme plus facilement intégrable en code :D.

Merci encore pour ta réponse claire et rapide =)

Skullkid
Habitué(e)
Messages: 3075
Enregistré le: 08 Aoû 2007, 20:08

par Skullkid » 21 Juil 2012, 13:18

L'algorithme marche à condition de faire attention aux cas où une intersection de la demi-droite partant du point considéré et le polygone est un sommet. Il suffit de ne pas compter cette intersection. Parmi les algorithmes possibles ça doit être le plus simple à coder... Mettons qu'on choisisse de prendre la demi-droite parallèle à l'axe des abscisses et qui s'étend vers les abscisses positives. Tu parcours les arêtes [AB] de ton polygone, tu ne sélectionnes que les arêtes telles que l'ordonnée de ton point est comprise entre les ordonnées de A et de B. Pour chacune de ces arêtes, tu calcules l'intersection de l'arête et de la droite horizontale qui passe par ton point de départ. Si l'intersection est un sommet (tu compares l'intersection à A et B) ou l'arête entière (l'ordonnée de A est égale à l'ordonnée de B qui est égale à l'ordonnée du point de départ) ou a une abscisse inférieure à celle du point de départ, tu ne la comptes pas. Sinon, tu la comptes et tu passes à l'arête suivante.

Dlzlogic
Membre Transcendant
Messages: 5273
Enregistré le: 14 Avr 2009, 13:39

par Dlzlogic » 21 Juil 2012, 13:36

ampholyte a écrit:Merci pour cette réponse,

Est-ce que cette algorithme est toujours vérifiée ou existe-t-il des cas particuliers ?

Existe-t-il également un algorithme plus facilement intégrable en code :D.

Merci encore pour ta réponse claire et rapide =)

Dans la réalité, le problème est un peu plus compliqué.
La raison tient au fait que tous les calculs en informatique sont faits avec un nombre fini de chiffres significatifs. Dans ce domaine, on ne peut pas tester l'égalité de deux réels, parce un réel, ça n'existe pas en informatique.

J'ai regardé mon module, je ne calcule pas les intersections, mais je repère le segment qui intersecte la verticale passant par le point.
Il y a aussi une autre technique qu'il ne faut pas négliger, qui consiste à trouver le côté le plus proche du point. Si on a pris la précaution d'orienter le polygone dans un sens conventionnel, la position du point, à droite ou à gauche du segment donnera le résultat.

Dans tous les cas, évitez à tout prix les calculs d'intersection, c'est un conseil.

Skullkid
Habitué(e)
Messages: 3075
Enregistré le: 08 Aoû 2007, 20:08

par Skullkid » 21 Juil 2012, 15:15

Dlzlogic a écrit:Dans la réalité, le problème est un peu plus compliqué.
La raison tient au fait que tous les calculs en informatique sont faits avec un nombre fini de chiffres significatifs. Dans ce domaine, on ne peut pas tester l'égalité de deux réels, parce un réel, ça n'existe pas en informatique.


On peut tout à fait tester l'égalité entre deux flottants, ou au pire comparer la différence de deux flottants à un epsilon petit donné...

Dlzlogic a écrit:J'ai regardé mon module, je ne calcule pas les intersections, mais je repère le segment qui intersecte la verticale passant par le point.


Il peut n'y avoir aucun côté du polygone qui intercepte la verticale, ou plusieurs... Mais une fois que tu les as, tu fais quoi ?

Dlzlogic a écrit:Il y a aussi une autre technique qu'il ne faut pas négliger, qui consiste à trouver le côté le plus proche du point. Si on a pris la précaution d'orienter le polygone dans un sens conventionnel, la position du point, à droite ou à gauche du segment donnera le résultat.


Cette technique ne marche que si le polygone est convexe, ou alors tu as une définition spéciale de "côté le plus proche d'un point".

Dlzlogic
Membre Transcendant
Messages: 5273
Enregistré le: 14 Avr 2009, 13:39

par Dlzlogic » 21 Juil 2012, 15:36

Skullkid a écrit:On peut tout à fait tester l'égalité entre deux flottants, ou au pire comparer la différence de deux flottants à un epsilon petit donné...

Il peut n'y avoir aucun côté du polygone qui intercepte la verticale, ou plusieurs... Mais une fois que tu les as, tu fais quoi ?

Cette technique ne marche que si le polygone est convexe, ou alors tu as une définition spéciale de "côté le plus proche d'un point".

Je suppose que tu as une grande expérience de ce type de test.
Bien-sûr on peut tester si 2 flottants sont égaux, mais la question n'est pas de savoir si on peut ou non le faire, mais de savoir si le test est toujours valide.
Pour t'en convaincre, fait le petit test suivant :
float c=3.0/7.0;
if (c == 3.0/7.0) printf("Egal\n");
else printf("Différent !!\n");

Je n'ai pas tout détaillé, j'avoue que je ne souviens plus des détails, mais si ça intéresse notre ami, je lui donnerai volontiers ma fonction.
Il est vrai que la notion "côté plus proche d'un point " serait à préciser. Je ne pense pas avoir dit que la projection du point sur le côté devait appartenir au segment.

C'est une question difficile qu'on ne peut pas régler en 5 lignes.
Que penses-tu par exemple des polygones avec trou?

Avatar de l’utilisateur
fatal_error
Modérateur
Messages: 6610
Enregistré le: 22 Nov 2007, 13:00

par fatal_error » 21 Juil 2012, 16:35

rien d'exceptionnel a gérer l'epsilone, mais des tests corrects, c'est mieux!

float c=3.0/7.0;
if (c == 3.0f/7.0f) printf("Egal\n");
else printf("Différent !!\n");
la vie est une fête :)

Avatar de l’utilisateur
ampholyte
Membre Transcendant
Messages: 3940
Enregistré le: 21 Juil 2012, 08:03

par ampholyte » 21 Juil 2012, 16:53

Bonjour,

Merci de vos précieux conseils.

Je vais essayer d'expliquer un peu plus en quoi consisterait mon script.

Supposons que j'ai un polygone donné (qu'on notera A), je souhaiterais insérer un polygone extrait d'une base de donnée (noté B), pour l'insérer dans le polygone A tout en vérifiant qu'il est inclut dans le polygone B.

Le petit schéma explicatif :D

Image

C'est pour cette raison que j'essaye de trouver un moyen de connaître l'inclusion ou non d'un point dans un polygone. J'aurais juste à parcourir l'ensemble des points de mon polygone B pour savoir s'il est inclus ou non dans A

Dlzlogic
Membre Transcendant
Messages: 5273
Enregistré le: 14 Avr 2009, 13:39

par Dlzlogic » 21 Juil 2012, 16:56

Aurais-je dit qu'il ne fallait pas gérer l'epsilon? C'est même indispensable lorsqu'on travaille avec des valeur qui ne sont pas que des entiers. Et c'est pas "au pire" c'est indispensable.
Je n'ai pas compris en quoi ce test n'est pas correct.
Ah j'ai compris, c'est le 'f' qui manque à ton avis ? Je ne sais pas quel langage impose ce suffixe 'f'.
Mon compilateur accepte aussi le 'f', il est vrai que dans ce cas la comparaison est bonne, mais si tu comptes sur ces astuces de langages, bonjour les dégâts.
Naturellement tu ignores l'origine de ce test, pourquoi son auteur a éprouvé le besoin de le mettre etc. Tu as juste cherché à montrer que j'avais tort.
D'ailleurs, as-tu la moindre idée de la raison du résultat "différent" ?

Dlzlogic
Membre Transcendant
Messages: 5273
Enregistré le: 14 Avr 2009, 13:39

par Dlzlogic » 21 Juil 2012, 17:02

@ ampholyte,
Ce que vous cherchez à faire est assez compliqué. Ce n'est pas parce que tous les angles du polygone sont intérieur que le polygone entier est intérieur.
Vous auriez plutôt intérêt à tester si les côtés n'ont aucune intersection, en ce cas le polygone B est soit intérieur, soit extérieur, et ensuite tester 1 sommet.
En tout cas, c'est ce que je ferais.

Skullkid
Habitué(e)
Messages: 3075
Enregistré le: 08 Aoû 2007, 20:08

par Skullkid » 21 Juil 2012, 17:03

Dlzlogic a écrit:Je suppose que tu as une grande expérience de ce type de test.
Bien-sûr on peut tester si 2 flottants sont égaux, mais la question n'est pas de savoir si on peut ou non le faire, mais de savoir si le test est toujours valide.


D'où l'utilisation d'un epsilon le cas échéant. Ou alors on prend un type plus adapté, genre le type BigDecimal en Java. Si bien sûr on peut s'abstenir de faire des comparaisons, c'est mieux, mais je ne connais pas d'algorithme qui parvienne à s'en passer pour ce problème-ci. Pour rester sur celui que j'ai donné - qui ne teste d'ailleurs jamais l'égalité de deux flottants issus d'un calcul, uniquement leur ordre - le seul cas éventuellement problématique surviendrait si le point est trop proche d'un des côtés, auquel cas le code renverrait "dedans" ou "dehors" (ce qui n'est pas complètement choquant puisqu'un point appartenant à un côté n'est ni à l'intérieur ni à l'extérieur du polygone) au hasard des arrondis. Si on veut un résultat uniforme on met l'epsilon et on décide si on veut que les côtés soient comptés comme faisant partie du polygone, mais en pratique ça ne change rien.

Dlzlogic a écrit:Je n'ai pas tout détaillé, j'avoue que je ne souviens plus des détails, mais si ça intéresse notre ami, je lui donnerai volontiers ma fonction.


Va donc regarder plus précisément... quel est l'intérêt de ton post si tu dis que tu as mieux mais que tu ne le montres pas ?

Dlzlogic a écrit:Il est vrai que la notion "côté plus proche d'un point " serait à préciser. Je ne pense pas avoir dit que la projection du point sur le côté devait appartenir au segment.


En maths, le côté le plus proche d'un point sera celui tel que la distance entre le point et son projeté orthogonal sur la droite supportant ledit côté soit minimale. Il n'y a évidemment pas besoin que ce projeté appartienne au côté. Mais encore une fois ça ne marche que si le polygone est convexe.

Dlzlogic a écrit:C'est une question difficile qu'on ne peut pas régler en 5 lignes.
Que penses-tu par exemple des polygones avec trou?


Qu'ils sont comme les autres. Si le point est dans un trou, il n'est pas dans le polygone et il y aura donc un nombre impair d'intersections.

ampholyte a écrit:J'aurais juste à parcourir l'ensemble des points de mon polygone B pour savoir s'il est inclus ou non dans A


Hum là en revanche tu as l'air de supposer que ton polygone A est convexe, sauf si tu veux vraiment parcourir tous les points (enfin, des points peu espacés) du polygone B, côtés inclus ?

Avatar de l’utilisateur
fatal_error
Modérateur
Messages: 6610
Enregistré le: 22 Nov 2007, 13:00

par fatal_error » 21 Juil 2012, 18:11

Aurais-je dit qu'il ne fallait pas gérer l'epsilon? C'est même indispensable lorsqu'on travaille avec des valeur qui ne sont pas que des entiers. Et c'est pas "au pire" c'est indispensable.

Non c'est encore faux!!!!!
un float est un float et ton compilateur sait comparer deux floats. Un double est un double et ton compilateur sait aussi comparer deux doubles. Il n'y a pas de oui mais. Le fait est : ce n'est pas indispensable de passer par de l'epsilone, éventuellement best practice, éventuellement bad practice que de comparer deux floats, mais ce n'est pas faux de le faire.

Je n'ai pas compris en quoi ce test n'est pas correct.
Ah j'ai compris, c'est le 'f' qui manque à ton avis ? Je ne sais pas quel langage impose ce suffixe 'f'.

Ben ce langage, c'est ptet celui que ton compilateur compile...
Mon compilateur accepte aussi le 'f', il est vrai que dans ce cas la comparaison est bonne, mais si tu comptes sur ces astuces de langages, bonjour les dégâts.

C'est pas une astuce de langage, c'est la connaissance de ton langage. Tu viens prêcher des inepties alors que tu t'assois sur les types de base.

Naturellement tu ignores l'origine de ce test, pourquoi son auteur a éprouvé le besoin de le mettre etc. Tu as juste cherché à montrer que j'avais tort.
D'ailleurs, as-tu la moindre idée de la raison du résultat "différent" ?

Oui tu as tord. Je t'ai montré pourquoi. Mais au lieu de chercher pourquoi, tu insistes dans tes erreurs.

Pour revenir au problème (ca fait depuis qq temps que Dlzlogic bassine le float c'est le mal, donc j'espère que ca sera réglé cette fois...).
L'algo proposé par skullkid s'apparente au cross number algorithm .

En revanche, l'algo pour placer le polygon B dans le polygon A, c'est une autre paire de manche!
la vie est une fête :)

Dlzlogic
Membre Transcendant
Messages: 5273
Enregistré le: 14 Avr 2009, 13:39

par Dlzlogic » 21 Juil 2012, 19:25

Bon, je pense qu'il faut essayer de rester calme.
Il y a un point incontournable, c'est la comparaison de deux flottants, qu'ils soient float, double ou big, ça ne change rien, cette comparaison ne doit être faite qu'avec un epsilon. L'expression "le cas échant" est un peu étonnante : quand introduira-t-on ce test, qu'est-ce qui provoquera la décision de l'introduire.
Il en est de même pour un point très proche d'un côté. Il est exactement dessus, à droite ou à gauche ? Il est fréquent et normal de prévoir la possibilité d'une bande axée sur le côté, mais ça ne fait que transposer ce problème.
Naturellement le test avec les intersection est valable, mais il ne faut pas oublier que si on fait une comparaison de deux flottants, il faut prendre des précautions. C'est le seul point sur lequel j'ai voulu insister.

Le test que j'ai donné, c'est pas moi qui l'ai fait. Celui qui l'a fait s'en est servi pour permettre de visualiser quelque-chose, pas plus. Ce n'est pas parce que un test peut être détourné que ce qu'il essaye de montrer est faux.
Si vous voulez un autre test, il suffit de prendre 2 droites, calculer leur intersection, Ca donne un point et tester si la distance de ce point à chacune des droites est égale à 0.

Je sais que ce problème est difficile. Une fois résolu, la position d'un polygone par rapport à un autre polygone, c'est de la rigolade.

Code: Tout sélectionner
bool TLigne::AppZone(TPointLong pXY, bool strict) const
{
   if (prem == NULL) return false;  // pas forcément une erreur
// version de base
// la version écran est identique, mais le calcul est fait en pixels
// un point appartenant au contour est considéré comme intérieur
// la ligne peut être une Zone (Aire > 0)
// ou une ligne fermée
//if (xm == 625214269 && ym ==151169720)
//{
//  bool imp=true;
//}
//Todo:On pourrait rajouter les test de tolérence de bord (Zones)
  long xm=pXY.x;
  long ym=pXY.y;
  long oyH=MAXLONG; // Point en haut donc y plus grand que ym
  long oyB=-MAXLONG;  // Point en bas donc y plus petit que ym
  long x1,y1,x2,y2;
  long yc;
//fprintf(espion,"AppZone xm=%d  ym=%d  \n",xm,ym);
//fprintf(espion,"Appel de Aire 11: prem(%p)\n",LitPrem());
//fflush(espion);
  float A=LitAire(true);
  if (A == -1) return false;
      Point3D *pt1=PtPrem();  //PtSuiv((TMatricule*)NULL);
      x1=pt1->ValX();
      y1=pt1->ValY();
      Point3D *pt2, *pt1H=NULL, *pt2H=NULL, *pt1B=NULL, *pt2B=NULL ;
      SListe *lpt=prem;
      char cma='*';
      for (lpt=lpt->suiv; lpt; lpt=lpt->suiv)
      {
        pt2=PtCour(lpt);
        x2=pt2->ValX();
        y2=pt2->ValY();
        if (strchr("*#",cma))   // le code du point pt1
        {
//fprintf(espion,"AppZone x1=%d  y1=%d  x2=%d y2=%d\n",x1,y1,x2,y2);
          if (( xm >= x1 && xm  x2 && xm  labs (xm-x1))
            {
              double xm1=xm-x1;
              double y21=y2-y1;
              double x21=x2-x1;
              double ym21=xm1*y21/x21;
              yc=y1+ym21;
            }
            else
            {
              double xm1=xm-x2;
              double y12=y1-y2;
              double x12=x1-x2;
              double ym12=xm1*y12/x12;
              yc=y2+ym12;
            }
// pour résoudre le problème de points sur la ligne
            if (strict)
            {
              if (labs(ym-yc)  oyB)
              {
                oyB=yc;
                pt1B=pt1;
                pt2B=pt2;
              }
              if (yc-tUB > ym && yc/*+tUB*/  oyB)
              {
                oyB=yc;
                pt1B=pt1;
                pt2B=pt2;
              }
              if (yc+tUB > ym && yc-tUB cma;
      }
      if (oyH != MAXLONG  && oyB != -MAXLONG )
      {
        int IPV=IProdVect(pXY,pt1H->XY(),pt2H->XY());
        if ( A > 0 && IPV > 0 || A XY(),pt2B->XY());
          if ( A > 0 && IPV > 0 || A < 0 && IPV < 0)    return true;
          else return false;
        }
        else return false;
      }
      else   return false;
}

Bien sûr je détaillerai et j'expliquerai tout ce qu'on veux.

Avatar de l’utilisateur
fatal_error
Modérateur
Messages: 6610
Enregistré le: 22 Nov 2007, 13:00

par fatal_error » 21 Juil 2012, 19:37

ce n'est pas indispensable de passer par de l'epsilone

Il y a un point incontournable, c'est la comparaison de deux flottants, qu'ils soient float, double ou big, ça ne change rien, cette comparaison ne doit être faite qu'avec un epsilon

:marteau:

il faut prendre des précautions

Ca ne signifie >>>>>PAS<<<<< passer par des epsilones.
la vie est une fête :)

Avatar de l’utilisateur
fatal_error
Modérateur
Messages: 6610
Enregistré le: 22 Nov 2007, 13:00

par fatal_error » 21 Juil 2012, 19:40

Bien sûr je détaillerai et j'expliquerai tout ce qu'on veux.

est-il possible que tu fournisses l'algorithme en pseudo code?
et expliquer les paramètres dont se sert la méthode, et ce que fait la méthode?
la vie est une fête :)

Dlzlogic
Membre Transcendant
Messages: 5273
Enregistré le: 14 Avr 2009, 13:39

par Dlzlogic » 21 Juil 2012, 19:53

Si le but est de trouver un moyen de dire que j'ai tort, NON
Si c'est pour aider la communauté OUI.
Dans tous les cas, je répondrai aux questions, Mais la rédaction d'un pseudo-code ouvre naturellement la porte à toutes les critiques, qu'elles soient justifiées ou non.
De toute façon, ça me demandera du temps, donc, dans tous les cas, pas avant demain PM.

Skullkid
Habitué(e)
Messages: 3075
Enregistré le: 08 Aoû 2007, 20:08

par Skullkid » 21 Juil 2012, 20:16

Et donc ce code ne calcule pas d'intersection, il stocke juste les coordonnées du point d'intersection entre la verticale au point d'entrée et un côté pour faire joli ?

Dlzlogic a écrit:
Code: Tout sélectionner
[...]
            if (labs(xm-x2) > labs (xm-x1))
            {
              double xm1=xm-x1;
              double y21=y2-y1;
              double x21=x2-x1;
              double ym21=xm1*y21/x21;
              yc=y1+ym21;
            }
            else
            {
              double xm1=xm-x2;
              double y12=y1-y2;
              double x12=x1-x2;
              double ym12=xm1*y12/x12;
              yc=y2+ym12;
            }
[...]


Dlzlogic a écrit:J'ai regardé mon module, je ne calcule pas les intersections, mais je repère le segment qui intersecte la verticale passant par le point.

Dlzlogic
Membre Transcendant
Messages: 5273
Enregistré le: 14 Avr 2009, 13:39

par Dlzlogic » 21 Juil 2012, 20:26

Je pense que c'est mal parti pour le détail demandé par Fatal-error..
Selon mes notes, la dernière modif date de 2004, tes sarcasmes me paraissent un peu déplacés.
Peux-tu montrer une fonction que tu as faite et qui tourne depuis 8 ans sans modif ?

Skullkid
Habitué(e)
Messages: 3075
Enregistré le: 08 Aoû 2007, 20:08

par Skullkid » 21 Juil 2012, 20:54

Mes sarcasmes portent sur la description fausse que tu fais de ton propre code. Peu importe que ce ne soit pas toi qui l'aies écrit à la base, tu prétends savoir comment il fonctionne, et on se rend compte que non, quelle surprise ! Ça n'a évidemment rien à voir avec la date de dernière modification, mais bon on a l'habitude de tes réparties idiotes et désespérées...

 

Retourner vers ✯✎ Supérieur

Qui est en ligne

Utilisateurs parcourant ce forum : Aucun utilisateur enregistré et 51 invités

Tu pars déja ?



Fais toi aider gratuitement sur Maths-forum !

Créé un compte en 1 minute et pose ta question dans le forum ;-)
Inscription gratuite

Identification

Pas encore inscrit ?

Ou identifiez-vous :

Inscription gratuite