生成随机2D多边形的算法

时间:2012-01-25 02:16:59

标签: algorithm matlab random polygon computational-geometry

我不确定如何解决这个问题。我不确定它的任务有多复杂。我的目标是拥有一个生成任何多边形的算法。我唯一的要求是多边形不复杂(即边不相交)。我正在使用Matlab进行数学运算,但欢迎任何抽象的东西。

任何援助/指示?

编辑:

我正在考虑更多可以生成任何多边形的代码,即使这样:

enter image description here

5 个答案:

答案 0 :(得分:24)

我采用了@MitchWheat和@ templatetypedef关于在圆圈上采样点的想法,并把它放得更远。

在我的应用程序中,我需要能够控制多边形的奇怪程度,即从常规多边形开始,当我调高参数时,它们变得越来越混乱。基本思路如@templatetypedef所述;每次以一个随机的角度步长绕着圆圈行走,并且在每个步骤中以随机半径放置一个点。在方程式I中,生成角度步长为 equations for the angles and radii of the vertices

其中theta_i和r_i给出每个点相对于中心的角度和半径,U(min,max)从均匀分布中拉出一个随机数,N(mu,sigma)从高斯分布中提取一个随机数和clip(x,min,max)将值阈值化为一个范围。这给了我们两个非常好的参数来控制多边形的多么狂野 - 我称之为不规则的epsilon控制点是否在圆周上均匀地成角度空间,以及我和#sigma 39; ll调用 spikeyness ,它控制点与半径r_ave的圆度有多大差异。如果你将这两个都设置为0,那么你会获得完全规则的多边形,如果你将它们调高,那么多边形会变得更加疯狂。

我在python中快速掀起了这个并得到了这样的东西: 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.

答案 1 :(得分:21)

利用MATLAB类DelaunayTriTriRep以及它们用于处理三角网格的各种方法,有一种巧妙的方法可以做你想要的。下面的代码遵循以下步骤来创建任意simple polygon

  • 生成一些等于所需边数加上软糖因子的随机点。软糖因子确保,无论三角测量的结果如何,我们都应该有足够的面,以便能够将三角形网格修剪成具有所需边数的多边形。

  • 创建点的Delaunay三角剖分,从而得到一个convex polygon,它由一系列三角形面构成。

  • 如果三角剖分的边界具有比期望更多的边缘,则在具有唯一顶点的边缘上选择随机三角形面(即,三角形仅与三角剖分的其余部分共享一条边)。删除此三角形面将减少边界边缘的数量。

  • 如果三角测量边界的边缘少于所需的边缘,或者前一步骤无法找到要移除的三角形,则在边缘上选择一个随机三角形面,该边缘在三角剖分边界上只有一个边缘。删除此三角形面将增加边界边缘的数量。

  • 如果找不到符合上述标准的三角形面,则发出警告,指出无法找到具有所需边数的多边形,并返回当前三角剖分边界的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

生成的多边形可以是凸面或concave,但是对于更大数量的所需边,它们几乎肯定是凹的。多边形也是从单位正方形内随机生成的点生成的,因此具有较大边数的多边形通常看起来像具有“方形”边界(例如上面右下方的50边多边形)。要修改此常规边界形状,您可以更改随机选择初始xy点的方式(即从高斯分布等)。

答案 2 :(得分:10)

对于凸出的2D多边形(完全在我的头顶):

  1. 生成随机半径R

  2. 在Radius R圆周上生成N个随机点

  3. 围绕圆圈移动并在圆圈上的相邻点之间绘制直线。

答案 3 :(得分:1)

这是Mike Ounsworth解决方案的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

答案 4 :(得分:0)

如何生成随机点,然后弄清楚以什么顺序放置它们,以使边缘不重叠呢?我还没有做太多的研究,所以可能并不总是可能的,但是我认为是...(请让我知道是否,如果可以,请指出不正确的地方)。合适的位置可能会丢失或重新生成。)我也不知道是否有一种有效的方法可以对大量点执行此操作,但至少对于少量点应该很快。