matlab 二维随机多边形生成算法

uemypmqf  于 2023-03-19  发布在  Matlab
关注(0)|答案(6)|浏览(208)

我不知道如何处理这个问题。我不知道这是一个多么复杂的任务。我的目标是有一个算法,生成任何多边形。我唯一的要求是多边形是不复杂的(即边不相交)。我用Matlab做数学,但任何抽象的欢迎。
有什么帮助/指示吗?
编辑:
我想的更多的代码可以生成任何多边形甚至像这样的东西:

txu3uszq

txu3uszq1#

我采用了@MitchWheat和@ templatypedef在圆上采样点的想法,并将其进一步扩展。
在我的应用程序中,我需要能够控制多边形有多奇怪,即从正多边形开始,随着参数的增加,它们变得越来越混乱。绕着圆走一圈,每次走一个随机的Angular 步长,在每一步中,在一个随机的半径上放置一个点,在方程中,我生成的Angular 步长为x1c 0d1x
其中theta_i和r_i给予每个点相对于中心U的Angular 和半径(min,max)从均匀分布中抽取随机数,并且N(mu,sigma)从高斯分布中提取随机数,并修剪(x,最小值,max)将一个值限定在一个范围内。这给了我们两个非常好的参数来控制多边形的狂野程度- epsilon,我称之为不规则性控制这些点是否均匀分布在圆的周围,sigma,我称之为,尖峰度,控制这些点与半径为r_ave的圆的偏差,如果你把这两个参数都设为0,你会得到完全规则的多边形,如果你把它们调大,多边形会变得更疯狂。
我用python很快地把它做出来,得到了这样的东西:

下面是完整的python代码:

import math, random
from typing import List, Tuple

def generate_polygon(center: Tuple[float, float], avg_radius: float,
                     irregularity: float, spikiness: float,
                     num_vertices: int) -> List[Tuple[float, float]]:
    """
    Start with the center of the polygon at center, then creates the
    polygon by sampling points on a circle around the center.
    Random noise is added by varying the angular spacing between
    sequential points, and by varying the radial distance of each
    point from the centre.

    Args:
        center (Tuple[float, float]):
            a pair representing the center of the circumference used
            to generate the polygon.
        avg_radius (float):
            the average radius (distance of each generated vertex to
            the center of the circumference) used to generate points
            with a normal distribution.
        irregularity (float):
            variance of the spacing of the angles between consecutive
            vertices.
        spikiness (float):
            variance of the distance of each vertex to the center of
            the circumference.
        num_vertices (int):
            the number of vertices of the polygon.
    Returns:
        List[Tuple[float, float]]: list of vertices, in CCW order.
    """
    # Parameter check
    if irregularity < 0 or irregularity > 1:
        raise ValueError("Irregularity must be between 0 and 1.")
    if spikiness < 0 or spikiness > 1:
        raise ValueError("Spikiness must be between 0 and 1.")

    irregularity *= 2 * math.pi / num_vertices
    spikiness *= avg_radius
    angle_steps = random_angle_steps(num_vertices, irregularity)

    # now generate the points
    points = []
    angle = random.uniform(0, 2 * math.pi)
    for i in range(num_vertices):
        radius = clip(random.gauss(avg_radius, spikiness), 0, 2 * avg_radius)
        point = (center[0] + radius * math.cos(angle),
                 center[1] + radius * math.sin(angle))
        points.append(point)
        angle += angle_steps[i]

    return points
def random_angle_steps(steps: int, irregularity: float) -> List[float]:
    """Generates the division of a circumference in random angles.

    Args:
        steps (int):
            the number of angles to generate.
        irregularity (float):
            variance of the spacing of the angles between consecutive vertices.
    Returns:
        List[float]: the list of the random angles.
    """
    # generate n angle steps
    angles = []
    lower = (2 * math.pi / steps) - irregularity
    upper = (2 * math.pi / steps) + irregularity
    cumsum = 0
    for i in range(steps):
        angle = random.uniform(lower, upper)
        angles.append(angle)
        cumsum += angle

    # normalize the steps so that point 0 and point n+1 are the same
    cumsum /= (2 * math.pi)
    for i in range(steps):
        angles[i] /= cumsum
    return angles
def clip(value, lower, upper):
    """
    Given an interval, values outside the interval are clipped to the interval
    edges.
    """
    return min(upper, max(value, lower))

@MateuszKonieczny这是从一组顶点创建多边形图像的代码。

vertices = generate_polygon(center=(250, 250),
                            avg_radius=100,
                            irregularity=0.35,
                            spikiness=0.2,
                            num_vertices=16)

black = (0, 0, 0)
white = (255, 255, 255)
img = Image.new('RGB', (500, 500), white)
im_px_access = img.load()
draw = ImageDraw.Draw(img)

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

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

img.show()

# now you can save the image (img), or do whatever else you want with it.
pw136qt2

pw136qt22#

利用MATLAB类DelaunayTriTriRep以及它们用于处理三角形网格的各种方法,有一种简洁的方法可以完成您想要的任务。下面的代码按照以下步骤创建一个任意的simple polygon

  • 生成一些随机点,其数量等于所需的边数加上一个fudge因子。fudge因子确保,无论三角剖分的结果如何,我们都应该有足够的面来将三角网格修剪成具有所需边数的多边形。
  • 创建这些点的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

下面是一些示例结果:

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

ymzxtsji

ymzxtsji3#

对于凸2D多边形(完全超出我的想象):
1.生成一个随机半径R
1.在半径为R的圆周上生成N个随机点
1.围绕圆移动,并在圆上的相邻点之间绘制直线。

bxgwgixi

bxgwgixi4#

正如@ templatypedef和@MitchWheat所说,生成N个随机Angular 和半径很容易做到这一点。对Angular 进行排序很重要,否则它将不是一个简单的多边形。注意,我使用了一个简洁的技巧来绘制闭合曲线--我在这里描述过。顺便说一下,多边形可能是凹的
请注意,所有这些多边形都是星星的。生成一个更一般的多边形根本不是一个简单的问题。只是为了给予你尝一尝这个问题的滋味-检查http://www.cosy.sbg.ac.at/~held/projects/rpg/rpg.htmlhttp://compgeom.cs.uiuc.edu/~jeffe/open/randompoly.html

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
lfapxunr

lfapxunr5#

这里是一个工作端口的Matlab的迈克Ounsworth解决方案。我没有优化它的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
8gsdolmq

8gsdolmq6#

有了现成的计算几何库,一种简单有效的方法是:

  • 生成随机点集
  • 计算点集的船体
  • (可选)平滑船体(以去除窄条等)

变凹度凹船体

带(高斯)平滑的凹面船体

相关问题