Я взял идею @MitchWheat и @ templatetypedef о точках выборки на окружности и взял ее немного дальше.
В моем приложении мне нужно иметь возможность контролировать, насколько странными являются полигоны, т.е. начинать с обычногополигоны и когда я проверяю параметры, они становятся все более хаотичными.Основная идея сформулирована @templatetypedef;ходить по кругу, делая каждый раз случайный угловой шаг, и на каждом шаге ставить точку со случайным радиусом.В уравнениях я генерирую угловые шаги как
, где theta_i и r_i дают угол и радиус каждой точки относительно центра, U (min, max) вытягивает случайное число из равномерного распределенияи N (mu, sigma) извлекает случайное число из гауссовского распределения, а clip (x, min, max) порождает значение в диапазоне.Это дает нам два действительно хороших параметра для контроля того, насколько дикими являются многоугольники - epsilon, который я назову нерегулярность , определяет, будут ли точки равномерно распределены по углу вокруг круга, и сигма, которую я назову spikeyness , который контролирует, насколько точки могут отличаться от круга радиуса r_ave.Если вы установите оба из них на 0, тогда вы получите совершенно правильные многоугольники, если вы их провернете, то эти многоугольники станут безумнее.
Я быстро поднял это в python и получил такие вещи:
Вот полный код 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.