Tri du tableau de points dans le sens des aiguilles d'une montre

16

Existe-t-il un tel algorithme pour trier un tableau de points 2D dans le sens horaire?
Je traite spécifiquement du triangle rectangle dans mon cas, donc seulement 3 points.

Cependant, je suis intéressé à savoir si un tel algorithme existe, sinon quel est un moyen simple de retourner les 3 points de mon triangle dans le sens horaire?

Edit: j'essaie de calculer les points dans le sens horaire par rapport au centre de gravité du polygone, qui est convexe.

Mise à jour: c'est l'implémentation que j'ai fini par utiliser en fonction de la réponse choisie, ce n'est pas critique en termes de performances et ne se produit que de temps en temps, donc ça marche.

ArrayList<PVector> pointList = new ArrayList<PVector>();
pointList.add(A);
pointList.add(B);
pointList.add(C);
Collections.sort( pointList, new TriangleVectorComparator(origin) );

return pointList;

// Comparator
package triangleeditor;

import java.util.Comparator;

import processing.core.PVector;

public class TriangleVectorComparator implements Comparator<PVector>  {
    private PVector M; 
    public TriangleVectorComparator(PVector origin) {
        M = origin;
    }

    public int compare(PVector o1, PVector o2) {
        double angle1 = Math.atan2(o1.y - M.y, o1.x - M.x);
        double angle2 = Math.atan2(o2.y - M.y, o2.x - M.x);

        //For counter-clockwise, just reverse the signs of the return values
        if(angle1 < angle2) return 1;
        else if (angle2 < angle1) return -1;
        return 0;
    }

}
onedayitwillmake
la source
1
le retour peut être l'angle de retour1 <l'angle2? 1: angle2> angle1? -dix;
ademar111190
2
Il peut être exprimé de plusieurs façons, mais j'ai tendance à éviter les opérateurs ternaires imbriqués, surtout lorsque je donne des exemples.
onedayitwillmake
@onedayitwillmake Pourriez-vous déplacer le code que vous avez fini par utiliser dans une réponse? Il ne fait pas vraiment partie de la question, mais il est très précieux pour les futurs lecteurs.
Anko
@Anko Je pense que vous avez raison, mais en même temps, je pense qu'il sera plus facile pour les gens de le trouver de cette façon. Cela permet également de s'assurer que je ne retire pas de la réponse de samhocevar sur laquelle la mienne est simplement basée.
onedayitwillmake

Réponses:

19

Votre question n'est pas assez précise. Un tableau de points est uniquement «dans le sens horaire» ou «anti-horaire» par rapport à un point de référence. Sinon, tout tableau de trois points peut toujours être CW ou CCW. Voir l'image suivante: à gauche, les points sont ordonnés dans le sens des aiguilles d'une montre; à droite, les mêmes points sont ordonnés dans le sens inverse des aiguilles d'une montre.

dans le sens horaire ou antihoraire

Dans votre cas, je pense qu'il est raisonnable d'utiliser le barycentre des points comme point de référence.

Une bonne méthode pour un nombre inconnu de points pourrait être la suivante:

  • laissez - P[0], P[1], ... P[n-1]être la liste des points pour trier
  • soit M le barycentre de tous les points
  • calculer de a[0], a[1], ... a[n-1]telle sorte quea[i] = atan2(P[i].y - M.y, P[i].x - M.x);
  • trier les points par rapport à leur avaleur, en utilisant qsortpar exemple.

Cependant, vous pouvez être sûr qu'un bon algorithme de tri fonctionnera mal avec trois valeurs d'entrée par rapport à une méthode ad hoc. L'utilisation atan2est toujours valide, mais ne l'utilisez pas qsort.

sam hocevar
la source
Cela a parfaitement fonctionné :)
onedayitwillmake
1
Cette méthode a-t-elle un nom?
onedayitwillmake
1
Je crois que cela s'appelle simplement «tri par angle polaire». C'est un composant du Graham Scan , par exemple.
sam hocevar
La performance atteinte qsortici est minuscule par rapport à atan2.
Petit avertissement: cela risque de planter et de graver (selon le langage de programmation et les bibliothèques utilisés) si l'un des points se trouve exactement au barycentre (ou tout autre point d'orientation que vous utilisez). Vous souhaiterez peut-être exclure ces points entre la deuxième et la troisième étape.
Martin Sojka
3

Je crois que ce que vous demandez en fait ici, c'est l'ordre d'enroulement du triangle, qui est en fait assez simple à tester.

Puisqu'il n'y a que trois points dans votre triangle, votre triangle est déjà dans un sens horaire ou antihoraire, et donc tout ce que vous devez faire est de vérifier lequel de ces deux il est, et inverser l'ordre des indices si le bobinage ce n'est pas celui que vous voulez.

Voici l'idée générale, en supposant que les trois sommets d'un triangle sont a , b et c , et que vous avez une opération de soustraction vectorielle simple:

Vector2 AToB = b - a;
Vector2 BToC = c - b;
float crossz = AToB.x * BToC.y - AToB.y * BToC.x;
if ( crossz > 0.0f )
{
  // clockwise
}
else
{
  // counter-clockwise.  Need to reverse the order of our vertices.
}

Notez que selon la façon dont vous avez orienté votre axe + y (vers le haut ou vers le bas), les cas "dans le sens horaire" et "anti-horaire" peuvent être inversés par rapport à la façon dont je les ai étiquetés dans les commentaires de cet exemple de code.

Trevor Powell
la source
J'ai besoin de transmettre ces points à Box2D (implémentation java) afin de créer un polygone. bien que ce ne soit pas ce que je demandais, très bon aperçu :)
onedayitwillmake
2

Pouvez-vous donner plus d'informations? Vous voulez un ordre de points CCW, mais quel point devrait être le centre de la commande?

Si vous n'avez qu'un triangle (3 points) dans le plan, vous pouvez calculer le déterminant à partir de la matrice, où les lignes sont les coordonnées des points (la 3e coordonnée est 1). Si le déterminant est> 0, les points sont dans l'ordre CCW. Sinon, vous pouvez par exemple passer les deux derniers points et vous obtiendrez l'ordre CCW.

Si vous avez des points A, B, C, alors votre matrice ressemble à:

|xA, yA, 1|
|xB, yB, 1|
|xC, yC, 1|

Le déterminant est: xA * yB + xB * yC + xC * yA - yB * xC - yC * xA - yA * xB. Ensuite, vous pouvez le comparer à zéro. Si c'est> 0, retournez les points A, B, C, sinon, retournez A, C, B.

Si vous avez défini des points et que vous savez qu'ils font un polygone convexe (tous font partie d'une coque convexe) et que vous souhaitez obtenir leur commande, vous pouvez utiliser Graham Scan ou Jarvis's March (ce sont des algorithmes pour trouver la coque convexe à partir de nombreux points, mais ça devrait aussi marcher ici :))

zacharmarz
la source
pour terminer ce que zacharmarz a dit si vous voulez simplement trier vos points dans le sens des aiguilles d'une montre autour d'un point spécifique, vous pouvez créer un tableau à partir de paires de flotteurs et de points dans lesquels vous stockez tous les points avec leur angle correspondant à partir de ce point spécifique, puis triez ce tableau.
Ali1S232