web-dev-qa-db-fra.com

Un algorithme pour gonfler/dégonfler des polygones

Comment pourrais-je "gonfler" un polygone? C'est-à-dire que je veux faire quelque chose de similaire à ceci: 

alt text

L’exigence est que les arêtes/points du nouveau polygone (gonflé) se trouvent tous à la même distance constante de celle du polygone ancien (original) oubliez ça pour l'instant;)). 

Le terme mathématique pour ce que je recherche est en fait polygone entrant/sortant offset. +1 à balint pour le signaler. La dénomination alternative est polygon tampon.

Résultats de ma recherche:

Voici quelques liens:

179
Igor Brejc

Je pensais pouvoir mentionner brièvement ma propre bibliothèque de découpage et de décalage de polygones - Clipper .

Bien que Clipper soit principalement conçu pour les opérations d’écrêtage de polygones, il effectue également la compensation de polygones. La bibliothèque est un logiciel libre open source écrit en Delphi, C++ et C # . Il possède une licence très libre Boost lui permettant d'être utilisé gratuitement dans les applications gratuites et commerciales.

Le décalage de polygone peut être effectué en utilisant l’un des trois styles de décalage suivants: carré, rond et en onglet.

Polygon offsetting styles

129
Angus Johnson

Le polygone que vous recherchez s'appelle polygone de décalage entrant/sortant dans la géométrie de calcul et il est étroitement lié au squelette droit .

Il existe plusieurs polygones décalés pour un polygone complexe:

Et voici le squelette d'un autre polygone:

Comme indiqué dans d'autres commentaires, vous pouvez également obtenir une connectivité différente pour la sortie en fonction de la distance à laquelle vous envisagez de "gonfler/dégonfler" votre polygone. 

Du point de vue du calcul: une fois que vous avez le squelette droit, vous devriez être capable de construire les polygones de décalage relativement facilement. La bibliothèque open source et (gratuite pour les entreprises non commerciales) CGAL contient un package implémentant ces structures. Voir cet exemple de code pour calculer les polygones offset à l'aide de CGAL.

Le manuel du package devrait vous donner un bon point de départ pour la construction de ces structures, même si vous n'utilisez pas CGAL, et contient des références aux articles avec les définitions et propriétés mathématiques:

Manuel CGAL: Compensation squelette droite 2D et polygone

37
balint.miklos

Il me semble que ce que vous voulez c'est:

  • En partant d'un sommet, faites face dans le sens contraire des aiguilles d'une montre le long d'un bord adjacent.
  • Remplacez le bord par un nouveau bord parallèle placé à la distance d à la "gauche" de l'ancien.
  • Répétez pour tous les bords.
  • Recherchez les intersections des nouvelles arêtes pour obtenir les nouveaux sommets.
  • Détectez si vous êtes devenu un polynôme croisé et décidez quoi faire à ce sujet. Ajoutez probablement un nouveau sommet au point de croisement et éliminez certains anciens. Je ne sais pas s'il existe un meilleur moyen de détecter cela que de simplement comparer chaque paire d'arêtes non adjacentes pour voir si leur intersection se situe entre les deux paires de sommets.

Le polygone résultant se trouve à la distance requise de l'ancien polygone "assez loin" des sommets. Près d'un sommet, l'ensemble des points à la distance d de l'ancien polygone n'est, comme vous le dites, pas un polygone, de sorte que la condition énoncée ne peut être remplie.

Je ne sais pas si cet algorithme a un nom, un exemple de code sur le Web ou une optimisation diabolique, mais je pense qu'il décrit ce que vous voulez.

8
Steve Jessop

Pour ces types de choses, j’utilise habituellement JTS . À des fins de démonstration, j'ai créé ce jsFiddle qui utilise JSTS (port JavaScript de JTS). Il vous suffit de convertir les coordonnées dont vous disposez en coordonnées JSTS:

function vectorCoordinates2JTS (polygon) {
  var coordinates = [];
  for (var i = 0; i < polygon.length; i++) {
    coordinates.Push(new jsts.geom.Coordinate(polygon[i].x, polygon[i].y));
  }
  return coordinates;
}

Le résultat ressemble à ceci:

 enter image description here

Informations complémentaires: J'utilise généralement ce type de gonflage/dégonflage (un peu modifié pour mes besoins) pour définir des limites de rayon sur des polygones dessinés sur une carte (avec Leaflet ou Google Maps). Vous venez de convertir des paires (lat, lng) en coordonnées JSTS et tout le reste est identique. Exemple:

 enter image description here

7
Marko Letic

Chaque ligne doit diviser le plan en "dedans" et en "contour"; vous pouvez le découvrir en utilisant la méthode habituelle du produit interne.

Déplacez toutes les lignes vers l'extérieur d'une certaine distance. 

Considérez toutes les paires de lignes voisines (lignes, pas de segment de ligne), trouvez l'intersection. Ce sont le nouveau sommet.

Nettoyez le nouveau sommet en supprimant les parties qui se croisent. - nous avons quelques cas ici

(a) Cas 1:

 0--7  4--3
 |  |  |  |
 |  6--5  |
 |        |
 1--------2

si vous le dépensez par un, vous obtenez ceci:

0----a----3
|    |    |
|    |    |
|    b    |
|         |
|         |
1---------2

7 et 4 se chevauchent .. si vous voyez cela, vous supprimez ce point et tous les points entre les deux.

b) cas 2

 0--7  4--3
 |  |  |  |
 |  6--5  |
 |        |
 1--------2

si vous le dépensez par deux, vous obtenez ceci:

0----47----3
|    ||    |
|    ||    |
|    ||    |
|    56    |
|          |
|          |
|          |
1----------2

pour résoudre ce problème, vous devez vérifier, pour chaque segment de ligne, s'il se superpose à ces derniers.

(c) cas 3

       4--3
 0--X9 |  |
 |  78 |  |
 |  6--5  |
 |        |
 1--------2

dépenser de 1. C’est un cas plus général pour le cas 1.

d) cas 4

idem cas3, mais dépenser par deux.

En fait, si vous pouvez gérer le cas 4. Tous les autres cas ne sont que des cas particuliers avec un chevauchement de lignes ou de sommets.

Dans le cas 4, vous conservez une pile de sommets. Vous appuyez sur lorsque vous trouvez des lignes qui se chevauchent avec celle-ci, faites-la apparaître lorsque vous obtenez la dernière ligne. - juste comme ce que vous faites en coque convexe.

5
J-16 SDiZ

Voici une solution alternative, voyez si vous l'aimez mieux.

  1. Faites une triangulation , il n'est pas nécessaire que ce soit delaunay - n'importe quelle triangulation conviendrait.

  2. Gonflez chaque triangle - cela devrait être trivial. si vous enregistrez le triangle dans le sens inverse des aiguilles d'une montre, déplacez simplement les lignes à droite et faites l'intersection.

  3. Fusionnez-les en utilisant un algorithme de découpage modifié Weiler-Atherton

5
J-16 SDiZ

Dans le monde des SIG, on utilise la mise en mémoire tampon négative pour cette tâche: http://www-users.cs.umn.edu/~npramod/enc_pdf.pdf

La bibliothèque JTS devrait le faire pour vous. Voir la documentation sur le fonctionnement du tampon: http://tsusiatsoftware.net/jts/javadoc/com/vividsolutions/jts/operation/buffer/package-summary.html

Pour un aperçu général, voir également le Guide du développeur: http://www.vividsolutions.com/jts/bin/JTS%20Developer%20Guide.pdf

5
stryeko

Un grand merci à Angus Johnson pour sa bibliothèque de tondeuses . Il existe de bons exemples de code permettant de réaliser des extraits de coupures sur la page d'accueil de la tondeuse à l'adresse http://www.angusj.com/delphi/clipper.php#code mais je n’ai pas vu d’exemple pour la compensation de polygones. Alors j'ai pensé que peut-être que c'est utile pour quelqu'un si je poste mon code:

    public static List<Point> GetOffsetPolygon(List<Point> originalPath, double offset)
    {
        List<Point> resultOffsetPath = new List<Point>();

        List<ClipperLib.IntPoint> polygon = new List<ClipperLib.IntPoint>();
        foreach (var point in originalPath)
        {
            polygon.Add(new ClipperLib.IntPoint(point.X, point.Y));
        }

        ClipperLib.ClipperOffset co = new ClipperLib.ClipperOffset();
        co.AddPath(polygon, ClipperLib.JoinType.jtRound, ClipperLib.EndType.etClosedPolygon);

        List<List<ClipperLib.IntPoint>> solution = new List<List<ClipperLib.IntPoint>>();
        co.Execute(ref solution, offset);

        foreach (var offsetPath in solution)
        {
            foreach (var offsetPathPoint in offsetPath)
            {
                resultOffsetPath.Add(new Point(Convert.ToInt32(offsetPathPoint.X), Convert.ToInt32(offsetPathPoint.Y)));
            }
        }

        return resultOffsetPath;
    }
3
PainElemental

D'après les conseils de @ JoshO'Brian, il apparaît que le paquetage rGeos dans le langage R implémente cet algorithme. Voir rGeos::gBuffer.

1
Carl Witthoft

Une autre option consiste à utiliser boost :: polygon - la documentation manque quelque peu, mais vous devriez constater que les méthodes resize et bloat, ainsi que l’opérateur surchargé +=, qui implémentent la mise en mémoire tampon. Ainsi, par exemple, augmenter la taille d'un polygone (ou d'un ensemble de polygones) d'une valeur peut être aussi simple que:

poly += 2; // buffer polygon by 2
1
Paul R

Il existe plusieurs bibliothèques que l’on peut utiliser (utilisables également pour les ensembles de données 3D).

  1. https://github.com/otherlab/openmesh
  2. https://github.com/alecjacobson/nested_cages
  3. http://homepage.tudelft.nl/h05k3/Projects/MeshThickeningProj.htm

On peut également trouver les publications correspondantes de ces bibliothèques pour comprendre les algorithmes plus en détail.

Le dernier a le moins de dépendances et est autonome et peut lire les fichiers .obj.

Meilleurs voeux, Stephan

0
Stephan M. G.

J'utilise une géométrie simple: vecteurs et/ou trigonométrie

  1. À chaque coin, trouvez le vecteur moyen et l’angle moyen. Le vecteur médian est la moyenne arithmétique des deux vecteurs unitaires définis par les arêtes du coin. L'angle moyen est la moitié de l'angle défini par les arêtes.

  2. Si vous devez agrandir (ou contracter) votre polygone de la quantité de d de chaque bord vous devriez sortir du nombre d/sin (midAngle) pour obtenir le nouveau point de coin.

  3. Répétez cette opération pour tous les coins

*** Faites attention à votre direction. Faites un test CounterClockWise en utilisant les trois points définissant le coin; pour savoir quel chemin est dehors ou dedans.

0
user2800464