Проблема в том, что тип случайных фигур, показанных в вопросе, не является действительно случайным.Они как-то сглажены, упорядочены, на вид случайные формы.Хотя создавать действительно случайные формы с помощью компьютера легко, создание этих псевдослучайных форм намного проще, если использовать ручку и бумагу.
Таким образом, одним из вариантов является создание таких фигур в интерактивном режиме.Это показано в вопросе Интерактивная подгонка BSpline в Python .
Если вы хотите программно создавать случайные фигуры, мы можем адаптировать решение к Как соединять точки с учетом положенияи ориентация каждого из них с использованием кубических кривых Безье .
Идея состоит в том, чтобы создать набор случайных точек с помощью get_random_points
и вызвать функцию get_bezier_curve
с ними.Это создает набор кривых Безье, которые плавно связаны друг с другом в точках ввода.Мы также следим за тем, чтобы они были циклическими, то есть чтобы переход между начальной и конечной точкой также был плавным.
import numpy as np
from scipy.special import binom
import matplotlib.pyplot as plt
bernstein = lambda n, k, t: binom(n,k)* t**k * (1.-t)**(n-k)
def bezier(points, num=200):
N = len(points)
t = np.linspace(0, 1, num=num)
curve = np.zeros((num, 2))
for i in range(N):
curve += np.outer(bernstein(N - 1, i, t), points[i])
return curve
class Segment():
def __init__(self, p1, p2, angle1, angle2, **kw):
self.p1 = p1; self.p2 = p2
self.angle1 = angle1; self.angle2 = angle2
self.numpoints = kw.get("numpoints", 100)
r = kw.get("r", 0.3)
d = np.sqrt(np.sum((self.p2-self.p1)**2))
self.r = r*d
self.p = np.zeros((4,2))
self.p[0,:] = self.p1[:]
self.p[3,:] = self.p2[:]
self.calc_intermediate_points(self.r)
def calc_intermediate_points(self,r):
self.p[1,:] = self.p1 + np.array([self.r*np.cos(self.angle1),
self.r*np.sin(self.angle1)])
self.p[2,:] = self.p2 + np.array([self.r*np.cos(self.angle2+np.pi),
self.r*np.sin(self.angle2+np.pi)])
self.curve = bezier(self.p,self.numpoints)
def get_curve(points, **kw):
segments = []
for i in range(len(points)-1):
seg = Segment(points[i,:2], points[i+1,:2], points[i,2],points[i+1,2],**kw)
segments.append(seg)
curve = np.concatenate([s.curve for s in segments])
return segments, curve
def ccw_sort(p):
d = p-np.mean(p,axis=0)
s = np.arctan2(d[:,0], d[:,1])
return p[np.argsort(s),:]
def get_bezier_curve(a, rad=0.2, edgy=0):
""" given an array of points *a*, create a curve through
those points.
*rad* is a number between 0 and 1 to steer the distance of
control points.
*edgy* is a parameter which controls how "edgy" the curve is,
edgy=0 is smoothest."""
p = np.arctan(edgy)/np.pi+.5
a = ccw_sort(a)
a = np.append(a, np.atleast_2d(a[0,:]), axis=0)
d = np.diff(a, axis=0)
ang = np.arctan2(d[:,1],d[:,0])
f = lambda ang : (ang>=0)*ang + (ang<0)*(ang+2*np.pi)
ang = f(ang)
ang1 = ang
ang2 = np.roll(ang,1)
ang = p*ang1 + (1-p)*ang2 + (np.abs(ang2-ang1) > np.pi )*np.pi
ang = np.append(ang, [ang[0]])
a = np.append(a, np.atleast_2d(ang).T, axis=1)
s, c = get_curve(a, r=rad, method="var")
x,y = c.T
return x,y, a
def get_random_points(n=5, scale=0.8, mindst=None, rec=0):
""" create n random points in the unit square, which are *mindst*
apart, then scale them."""
mindst = mindst or .7/n
a = np.random.rand(n,2)
d = np.sqrt(np.sum(np.diff(ccw_sort(a), axis=0), axis=1)**2)
if np.all(d >= mindst) or rec>=200:
return a*scale
else:
return get_random_points(n=n, scale=scale, mindst=mindst, rec=rec+1)
Вы можете использовать эти функции, например, как
fig, ax = plt.subplots()
ax.set_aspect("equal")
rad = 0.2
edgy = 0.05
for c in np.array([[0,0], [0,1], [1,0], [1,1]]):
a = get_random_points(n=7, scale=1) + c
x,y, _ = get_bezier_curve(a,rad=rad, edgy=edgy)
plt.plot(x,y)
plt.show()
Мы можем проверить, как параметры влияют на результат.По сути, здесь нужно использовать 3 параметра:
rad
, радиус вокруг точек, в которых находятся контрольные точки кривой Безье.Это число относится к расстоянию между соседними точками и, следовательно, должно быть в диапазоне от 0 до 1. Чем больше радиус, тем четче черты кривой. edgy
, параметр для определения плавностикривая.Если 0, то угол кривой через каждую точку будет средним между направлением на соседние точки.Чем оно больше, тем больше угол будет определяться только одной соседней точкой.Кривая, следовательно, получает «более острый». n
количество случайных точек для использования.Конечно, минимальное количество очков - 3. Чем больше очков вы используете, тем больше возможностей получит форма;с риском создания наложения или петель на кривой.