web-dev-qa-db-ja.com

ランダムな2Dポリゴンを生成するアルゴリズム

この問題への取り組み方がわかりません。タスクがどれほど複雑かわかりません。私の目的は、ポリゴンを生成するアルゴリズムを用意することです。私の唯一の要件は、ポリゴンが複雑でない(つまり、側面が交差しない)ことです。私は数学を行うためにMatlabを使用していますが、抽象的なものは何でも大歓迎です。

援助/指示はありますか?

編集:

次のようなものでもポリゴンを生成できるコードをもっと考えていました。

enter image description here

36
s5s

MATLABクラス DelaunayTriTriRep とそれらが使用するさまざまなメソッドを利用することで、思い通りのことを行うためのきちんとした方法があります。三角形メッシュの処理。以下のコードは、これらの手順に従って任意の simple polygon を作成します。

  • 必要な辺の数とファッジ係数に等しい数のランダムな点を生成します。ファッジ係数により、三角形分割の結果に関係なく、三角形メッシュを必要な数の辺を持つポリゴンにトリミングできるように十分なファセットが必要になります。

  • ポイントのDelaunay三角形分割を作成し、一連の三角形のファセットから構成される 凸多角形 を作成します。

  • 三角形分割の境界に必要以上のエッジがある場合は、一意の頂点を持つエッジ上のランダムな三角形のファセットを選択します(つまり、三角形は残りの三角形分割と1つのエッジのみを共有します)。この三角形のファセットを削除すると、境界エッジの数が減ります。

  • 三角形分割の境界のエッジが必要な数より少ない場合、または前の手順で削除する三角形が見つからなかった場合は、三角形の境界にエッジが1つだけあるEdgeのランダムな三角形のファセットを選択します。この三角形のファセットを削除すると、境界エッジの数が増えます。

  • 上記の基準に一致する三角形のファセットが見つからない場合は、必要な数の辺を持つポリゴンが見つからなかったことを警告し、現在の三角形分割境界の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ポイントをランダムに選択する方法を変更できます(つまり、ガウス分布などから)。

24
gnovice

@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)は、値を範囲にしきい値設定します。これにより、ポリゴンのワイルドさを制御する2つの本当に良いパラメータが得られます-イプシロンと呼ばれるirregularityは、ポイントが円の周りに角度的に均一に間隔があるかどうかを制御し、シグマと呼ばれます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.
29
Mike Ounsworth

凸状の2Dポリゴンの場合(完全に頭の上部から外れています):

  1. ランダムな半径Rを生成する

  2. 半径Rの円の円周上にN個のランダムな点を生成します

  3. 円の周りを移動し、円の隣接する点の間に直線を描きます。

12
Mitch Wheat

@templatetypedefと@MitchWheatが言ったように、Nランダムな角度と半径を生成することで簡単にできます。角度をソートすることが重要です。そうしないと、単純なポリゴンにはなりません。閉じた曲線を描くために私はきちんとしたトリックを使用していることに注意してください-私はそれを here で説明しました。ちなみに、ポリゴンはconcaveかもしれません。

これらのポリゴンはすべて星型になることに注意してください。より一般的なポリゴンの生成は、単純な問題ではありません。ちょうど問題の味を与えるために http://www.cosy.sbg.ac.at/~held/projects/rpg/rpg.htmlhttp: //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
6

これは、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
1
MosGeo