Algorithme pour générer un polygone 2D aléatoire

Je ne sais pas comment aborder ce problème. Je ne suis pas sûr de la complexité de la tâche. Mon objectif est d'avoir un algorithme qui génère un polygone. Ma seule exigence est que le polygone n'est pas complexe (c.-à-d. les côtés ne se croisent pas). J'utilise Matlab pour faire les maths, mais tout ce qui est abstrait est le bienvenu.

une aide/direction?

EDIT:

je pensais plutôt au code qui pourrait générer n'importe quel polygone, même des choses comme ceci:

enter image description here

27
demandé sur s5s 2012-01-25 06:16:58

5 réponses

il y a une bonne façon de faire ce que vous voulez en profitant des classes MATLAB DelaunayTri et TriRep et les diverses méthodes qu'ils emploient pour manipuler les mailles triangulaires. Le code ci-dessous suit ces étapes pour créer un simple polygone :

  • générez un nombre de points aléatoires égal au nombre désiré de côtés plus un facteur de caramel. Le facteur fudge assure que, quel que soit le résultat de la triangulation, nous devrions avoir assez de facettes pour être en mesure de couper la maille triangulaire vers le bas à un polygone avec le nombre désiré de côtés.

  • créer une triangulation Delaunay des points, résultant en un polygone convexe qui est construit à partir d'une série de facettes triangulaires.

  • Si la limite de la la triangulation a plus d'arêtes que désiré, choisir une facette triangulaire aléatoire sur le bord qui a un sommet unique (i.e. le triangle ne partage qu'un bord avec le reste de la triangulation). L'enlèvement de cette facette triangulaire réduira le nombre de bordures.

  • si la limite de la triangulation a moins d'arêtes que désirée, ou l'étape précédente n'a pas été en mesure de trouver un triangle à enlever, choisir une facette triangulaire au hasard sur le bord qui a un seul de ses bords sur la limite de triangulation. L'enlèvement de cette facette triangulaire augmentera le nombre de bordures.

  • si aucune facette triangulaire ne peut être trouvée correspondant aux critères ci-dessus, postez un avertissement qu'un polygone avec le nombre voulu de côtés n'a pas pu être trouvé et retournez les coordonnées x et y de la limite actuelle de la triangulation. Sinon, continuer à enlever les facettes triangulaires jusqu'à ce que le nombre désiré de bords est atteint, puis renvoie les coordonnées x et y de la limite de triangulation.

Voici la fonction résultante:

function [x, y, dt] = simple_polygon(numSides)

    if numSides < 3
        x = [];
        y = [];
        dt = DelaunayTri();
        return
    end

    oldState = warning('off', 'MATLAB:TriRep:PtsNotInTriWarnId');

    fudge = ceil(numSides/10);
    x = rand(numSides+fudge, 1);
    y = rand(numSides+fudge, 1);
    dt = DelaunayTri(x, y);
    boundaryEdges = freeBoundary(dt);
    numEdges = size(boundaryEdges, 1);

    while numEdges ~= numSides
        if numEdges > numSides
            triIndex = vertexAttachments(dt, boundaryEdges(:,1));
            triIndex = triIndex(randperm(numel(triIndex)));
            keep = (cellfun('size', triIndex, 2) ~= 1);
        end
        if (numEdges < numSides) || all(keep)
            triIndex = edgeAttachments(dt, boundaryEdges);
            triIndex = triIndex(randperm(numel(triIndex)));
            triPoints = dt([triIndex{:}], :);
            keep = all(ismember(triPoints, boundaryEdges(:,1)), 2);
        end
        if all(keep)
            warning('Couldn''t achieve desired number of sides!');
            break
        end
        triPoints = dt.Triangulation;
        triPoints(triIndex{find(~keep, 1)}, :) = [];
        dt = TriRep(triPoints, x, y);
        boundaryEdges = freeBoundary(dt);
        numEdges = size(boundaryEdges, 1);
    end

    boundaryEdges = [boundaryEdges(:,1); boundaryEdges(1,1)];
    x = dt.X(boundaryEdges, 1);
    y = dt.X(boundaryEdges, 2);

    warning(oldState);

end

Et voici quelques exemples de résultats:

enter image description here

les polygones générés peuvent être convexes ou concaves , mais pour un plus grand nombre de côtés désirés ils seront presque certainement concaves. Les polygones sont aussi généré à partir de points générés au hasard à l'intérieur d'un carré unitaire, de sorte que les polygones ayant un plus grand nombre de côtés auront généralement l'air d'avoir une limite "carrée" (comme l'exemple en bas à droite ci-dessus avec le polygone à 50 côtés). Pour modifier cette forme limite générale, vous pouvez changer la façon dont les points initiaux x et y sont choisis au hasard (c.-à-d. à partir d'une distribution gaussienne, etc.).

18
répondu gnovice 2018-08-21 15:20:23

j'ai pris l'idée de @MitchWheat et @templatetypedef de points d'échantillonnage sur un cercle et je l'ai prise un peu plus loin.

dans mon application, je dois être capable de contrôler comment les polygones sont bizarres, c'est-à-dire de commencer avec des polygones réguliers et que j'augmente les paramètres ils deviennent de plus en plus chaotiques. L'idée de base est comme indiqué par @templatetypedef; marchez autour du cercle en faisant un pas angulaire aléatoire à chaque fois, et à chaque pas mettez un point à un rayon aléatoire. Dans les équations je suis générer les pas angulaires comme equations for the angles and radii of the vertices

où thetaii et r_i donnent l'angle et le rayon de chaque point par rapport au centre, U(min, max) tire un nombre aléatoire d'une distribution uniforme, et N(mu, sigma) tire un nombre aléatoire d'une distribution gaussienne, et clip(X, min, max) indique une valeur dans une plage. Cela nous donne deux paramètres vraiment agréable pour contrôler comment wild les polygones sont-epsilon que je vais appeler irrégularité contrôle si les points sont uniformément l'espace angulaire autour du cercle, et sigma que je vais appeler spikeyness qui contrôle combien les points peuvent varier du cercle de rayon r_ave. Si vous définissez ces deux à 0, puis vous obtenez parfaitement les polygones réguliers, si vous manivelle jusqu'alors les polygones obtenir plus fou.

j'ai préparé ça rapidement en python et j'ai des trucs comme ça: some polygons I generated

voici le code python complet:

import math, random

def generatePolygon( ctrX, ctrY, aveRadius, irregularity, spikeyness, numVerts ) :
'''Start with the centre of the polygon at ctrX, ctrY, 
    then creates the polygon by sampling points on a circle around the centre. 
    Randon noise is added by varying the angular spacing between sequential points,
    and by varying the radial distance of each point from the centre.

    Params:
    ctrX, ctrY - coordinates of the "centre" of the polygon
    aveRadius - in px, the average radius of this polygon, this roughly controls how large the polygon is, really only useful for order of magnitude.
    irregularity - [0,1] indicating how much variance there is in the angular spacing of vertices. [0,1] will map to [0, 2pi/numberOfVerts]
    spikeyness - [0,1] indicating how much variance there is in each vertex from the circle of radius aveRadius. [0,1] will map to [0, aveRadius]
    numVerts - self-explanatory

    Returns a list of vertices, in CCW order.
    '''

    irregularity = clip( irregularity, 0,1 ) * 2*math.pi / numVerts
    spikeyness = clip( spikeyness, 0,1 ) * aveRadius

    # generate n angle steps
    angleSteps = []
    lower = (2*math.pi / numVerts) - irregularity
    upper = (2*math.pi / numVerts) + irregularity
    sum = 0
    for i in range(numVerts) :
        tmp = random.uniform(lower, upper)
        angleSteps.append( tmp )
        sum = sum + tmp

    # normalize the steps so that point 0 and point n+1 are the same
    k = sum / (2*math.pi)
    for i in range(numVerts) :
        angleSteps[i] = angleSteps[i] / k

    # now generate the points
    points = []
    angle = random.uniform(0, 2*math.pi)
    for i in range(numVerts) :
        r_i = clip( random.gauss(aveRadius, spikeyness), 0, 2*aveRadius )
        x = ctrX + r_i*math.cos(angle)
        y = ctrY + r_i*math.sin(angle)
        points.append( (int(x),int(y)) )

        angle = angle + angleSteps[i]

    return points

 def clip(x, min, max) :
     if( min > max ) :  return x    
     elif( x < min ) :  return min
     elif( x > max ) :  return max
     else :             return x

@MateuszKonieczny voici du code pour créer une image d'un polygone à partir d'une liste de Sommets.

verts = generatePolygon( ctrX=250, ctrY=250, aveRadius=100, irregularity=0.35, spikeyness=0.2, numVerts=16 )

black = (0,0,0)
white=(255,255,255)
im = Image.new('RGB', (500, 500), white)
imPxAccess = im.load()
draw = ImageDraw.Draw(im)
tupVerts = map(tuple,verts)

# either use .polygon(), if you want to fill the area with a solid colour
draw.polygon( tupVerts, outline=black,fill=white )

# or .line() if you want to control the line thickness, or use both methods together!
draw.line( tupVerts+[tupVerts[0]], width=2, fill=black )

im.show()

# now you can save the image (im), or do whatever else you want with it.
20
répondu Mike Ounsworth 2015-05-30 22:07:51

pour un polygone 2D convexe (complètement sorti de ma tête):

  1. la génération aléatoire de rayon, R

  2. générer N points aléatoires sur la circonférence D'un cercle de rayon R

  3. faites le tour du cercle et tracez des lignes droites entre les points adjacents du cercle.

9
répondu Mitch Wheat 2012-01-25 02:23:15

comme @templatetypedef et @MitchWheat l'ont dit, il est facile de le faire en générant N angles aléatoires et rayons. Il est important de trier les angles, sinon il ne sera pas un simple polygone. Notez que j'utilise une astuce soignée pour dessiner des courbes fermées - Je l'ai décrit dans ici . D'ailleurs, les polygones pourraient être concave .

Notez que tous ces polygones sont en forme d'étoile. La génération d'un plus générale polygone n'est pas un problème simple. Juste pour vous donner un avant-goût du problème - découvrez http://www.cosy.sbg.ac.at/~held/projects/rpg/rpg.html et http://compgeom.cs.uiuc.edu/~jeffe/open/randompoly.html .

enter image description here

function CreateRandomPoly()
    figure();
    colors = {'r','g','b','k'};
    for i=1:5
        [x,y]=CreatePoly();
        c = colors{ mod(i-1,numel(colors))+1};
        plotc(x,y,c);
        hold on;
    end        
end

function [x,y]=CreatePoly()
    numOfPoints = randi(30);
    theta = randi(360,[1 numOfPoints]);
    theta = theta * pi / 180;
    theta = sort(theta);
    rho = randi(200,size(theta));
    [x,y] = pol2cart(theta,rho);    
    xCenter = randi([-1000 1000]);
    yCenter = randi([-1000 1000]);
    x = x + xCenter;
    y = y + yCenter;    
end

function plotc(x,y,varargin)
    x = [x(:) ; x(1)];
    y = [y(:) ; y(1)];
    plot(x,y,varargin{:})
end
5
répondu Andrey Rubshtein 2017-05-23 12:09:44

voici un port de travail pour Matlab of Mike Ounsworth solution. Je n'ai pas optimisé pour matlab. Je peut mettre à jour la solution plus tard.

function [points] = generatePolygon(ctrX, ctrY, aveRadius, irregularity, spikeyness, numVerts)

%{
Start with the centre of the polygon at ctrX, ctrY, 
then creates the polygon by sampling points on a circle around the centre. 
Randon noise is added by varying the angular spacing between sequential points,
and by varying the radial distance of each point from the centre.

Params:
ctrX, ctrY - coordinates of the "centre" of the polygon
aveRadius - in px, the average radius of this polygon, this roughly controls how large the polygon is, really only useful for order of magnitude.
irregularity - [0,1] indicating how much variance there is in the angular spacing of vertices. [0,1] will map to [0, 2pi/numberOfVerts]
spikeyness - [0,1] indicating how much variance there is in each vertex from the circle of radius aveRadius. [0,1] will map to [0, aveRadius]
numVerts - self-explanatory

Returns a list of vertices, in CCW order.

Website: https://stackoverflow.com/questions/8997099/algorithm-to-generate-random-2d-polygon
%}


    irregularity = clip( irregularity, 0,1 ) * 2*pi/ numVerts;
    spikeyness = clip( spikeyness, 0,1 ) * aveRadius;

    % generate n angle steps
    angleSteps = [];
    lower = (2*pi / numVerts) - irregularity;
    upper = (2*pi / numVerts) + irregularity;
    sum = 0;
    for i =1:numVerts
        tmp = unifrnd(lower, upper);
        angleSteps(i) = tmp;
        sum = sum + tmp;
    end

    % normalize the steps so that point 0 and point n+1 are the same
    k = sum / (2*pi);
    for i =1:numVerts
        angleSteps(i) = angleSteps(i) / k;
    end

    % now generate the points
    points = [];
    angle = unifrnd(0, 2*pi);
    for i =1:numVerts
        r_i = clip( normrnd(aveRadius, spikeyness), 0, 2*aveRadius);
        x = ctrX + r_i* cos(angle);
        y = ctrY + r_i* sin(angle);
        points(i,:)= [(x),(y)];
        angle = angle + angleSteps(i);
    end

end


function value = clip(x, min, max)
   if( min > max ); value = x; return; end
   if( x  < min ) ; value = min; return; end
   if( x  > max ) ; value = max; return; end
   value = x;
end
0
répondu MosGeo 2018-03-12 21:59:15