Подтвердить что ты не робот

Алгоритм генерации случайного двумерного многоугольника

Я не уверен, как подойти к этой проблеме. Я не уверен, насколько сложна задача. Моя цель - иметь алгоритм, который генерирует любой многоугольник. Моим единственным требованием является то, что многоугольник не является сложным (т.е. Стороны не пересекаются). Я использую Matlab для выполнения математических упражнений, но что-то абстрактное приветствуется.

Любая помощь/направление?

EDIT:

Я думал больше о коде, который мог бы генерировать любой многоугольник, даже такие вещи:

enter image description here

4b9b3361

Ответ 1

Есть отличный способ сделать то, что вы хотите, используя преимущества классов MATLAB DelaunayTri и TriRep и различных методов, которые они используют для обработки треугольных сеток. Код ниже следует за этими шагами, чтобы создать произвольный простой многоугольник:

  • Создайте количество случайных точек, равное требуемому количеству сторон плюс коэффициент выдумки. Коэффициент выдумки гарантирует, что, независимо от результата триангуляции, у нас должно быть достаточно граней, чтобы можно было обрезать треугольную сетку до многоугольника с требуемым количеством сторон.

  • Создайте триангуляцию Делоне для точек, в результате чего получится выпуклый многоугольник, построенный из серии треугольных граней.

  • Если граница триангуляции имеет больше ребер, чем нужно, выберите случайную треугольную грань на ребре, которое имеет уникальную вершину (т.е. Треугольник имеет только одно ребро с остальной частью триангуляции). Удаление этой треугольной грани уменьшит количество граничных ребер.

  • Если граница триангуляции имеет меньше ребер, чем необходимо, или предыдущий шаг не смог найти треугольник для удаления, выберите случайный треугольный фасет на ребре, у которого только один из его ребер на границе триангуляции. Удаление этой треугольной грани увеличит количество граничных ребер.

  • Если треугольные грани, соответствующие вышеуказанным критериям, не найдены, выведите предупреждение о том, что полигон с нужным количеством сторон не может быть найден, и верните координаты x и y текущей границы триангуляции. В противном случае продолжайте удалять треугольные грани до тех пор, пока не будет достигнуто желаемое количество ребер, затем верните координаты x и y границы триангуляции.

Вот результирующая функция:

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

И вот некоторые примеры результатов:

enter image description here

Сгенерированные полигоны могут быть либо выпуклыми, либо вогнутыми, но для большего числа желаемых сторон они почти наверняка будут вогнутыми. Полигоны также генерируются из точек, случайно сгенерированных в единичном квадрате, поэтому полигоны с большим числом сторон обычно выглядят так, как будто они имеют "квадратную" границу (например, в нижнем правом примере выше с 50-сторонним многоугольником). Чтобы изменить эту общую ограничивающую форму, вы можете изменить способ случайного выбора начальных точек x и y (т.е. Из распределения Гаусса и т.д.).

Ответ 2

Я взял @MitchWheat и @templatetypedef идею точек выборки на круге и немного отнесся к ней.

В моем приложении мне нужно иметь возможность контролировать, насколько странны полигоны, т.е. начать с правильных многоугольников, и когда я проверю параметры, они становятся все более хаотичными. Основная идея указана в @templatetypedef; передвигайтесь по кругу, беря случайный шаг angular каждый раз, и на каждом шаге ставьте точку на случайный радиус. В уравнениях я генерирую шаги angular как equations for the angles and radii of the vertices

где theta_i и r_i дают угол и радиус каждой точки относительно центра, U (min, max) вытягивает случайное число из равномерного распределения, а N (mu, sigma) вытягивает случайное число из гауссовского распределения, и клип (x, min, max) порождает значение в диапазоне. Это дает нам два действительно приятных параметра для контроля того, насколько дикими являются полигоны - эпсилон, который я назову неравномерность, определяет, равномерны ли точки равномерно по кругу, и сигма, которую я назову spikeyness, который контролирует, насколько точки могут меняться от круга радиуса r_ave. Если вы установили оба из них в 0, вы получите совершенно правильные многоугольники, если вы их подведете, то полигоны станут более сумасшедшими.

Я быстро взбивал это на питоне и получал такие вещи: some polygons I generated

Здесь полный код python:

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 - это код для создания изображения многоугольника из списка вершин.

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.

Ответ 3

Для выпуклого двумерного многоугольника (полностью с головы):

  • Создайте случайный радиус, R

  • Создайте N случайных точек на окружности окружности радиуса R

  • Перемещайтесь по кругу и рисуйте прямые линии между соседними точками на круге.

Ответ 4

Как сказали @templatetypedef и @MitchWheat, это легко сделать, создав N случайные углы и радиусы. Важно сортировать углы, иначе это будет не простой полигон. Обратите внимание, что я использую аккуратный трюк, чтобы нарисовать замкнутые кривые - я описал это в здесь. Кстати, многоугольники могут быть вогнутыми.

Обратите внимание, что все эти полигоны будут иметь звездообразную форму. Создание более общего многоугольника - не простая проблема. Просто чтобы вы почувствовали вкус проблемы - проверьте  http://www.cosy.sbg.ac.at/~held/projects/rpg/rpg.html и 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

Вот рабочий порт для решения Matlab Майка Оунсворта. Я не оптимизировал его для Matlab. Я мог бы обновить решение позже для этого.

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

Ответ 6

Как насчет генерации случайных точек, а затем выяснения, в каком порядке их разместить, чтобы ни одно из ребер не перекрывалось? Я не так много изучал, так что это не всегда возможно, но я думаю, что это... (Пожалуйста, дайте мне знать, если это не так, и если это так, точки, которые не соответствуют, могут быть отброшены или восстановлены Я также не уверен, существует ли эффективный способ сделать это для большого количества очков, но, по крайней мере, он должен быть очень быстрым для небольшого количества очков.