我正在为认知心理学写一个实验。我需要生成对一些属性进行任意控制的类似涂鸦的图形。
- 图形永远不会自相交
- 它必须关闭
- 它必须占据大部分绘图表面/画布
- 我必须能够将其导出为一系列点
此外,我需要在一定程度上控制它的复杂性。
到目前为止,我的一般方法是这样做:
- 将画布分成象限并沿每个轴生成交点。这通过确保图形必须进入每个象限来保证图形占据大部分区域。
- 我在每个象限中生成随机数量的点。
- 我随机选择一条线或一条曲线并按顺序连接每个点
这行得通,但是,它对自相交的控制为零,而且我已经知道我可能对此进行控制的方法是愚蠢的、蛮力的方法。我已经包含了代码,但是对于 SO 来说有点冗长。此外,我绝不会特别关注这个一般过程,这只是我自己能够梦想的。欢迎对我的逻辑提出任何建议/策略/批评。
import numpy as np
import aggdraw
from random import choice
from PIL import ImageDraw, Image
from PIL import ImagePath
# there are a few functions used here, like "angle_between" that I've not included for brevity; they're all just simple functions that do what they say, usually some trig
def generate_segment_positions(seg_count, x_offset, y_offset, avg_dist, dist_variance):
# this just ensures that the points I choose fall within the desired
# quadrant and are within certain distances of each other
min_pt_dist = avg_dist - dist_variance
max_pt_dist = avg_dist + dist_variance
q_pts_x = []
q_pts_y = []
while not len(q_pts_x) == seg_count:
x = from_range(x_offset, x_offset + 400)
try:
if not (x in range(q_pts_x[-1] - min_pt_dist, q_pts_x[-1] + min_pt_dist)):
q_pts_x.append(x)
except IndexError:
q_pts_x.append(x)
while not len(q_pts_y) == seg_count:
y = from_range(y_offset, y_offset + 400)
try:
if not (y in range(q_pts_y[-1] - min_pt_dist, q_pts_y[-1] + min_pt_dist)):
q_pts_y.append(y)
except IndexError:
q_pts_y.append(y)
q_points = []
for p in q_pts_x:
q_points.append((p, q_pts_y[q_pts_x.index(p)]))
return q_points
def generate_arc_controls(dest, origin, quadrant):
# this is.. my attempting to work out some control over whether or not
# the curves I create go all over hell and creation... it doesn't work
# but I'm hoping it offers some insight into what I'm trying to do
m = midpoint(dest, origin)
rotation = int(angle_between(dest, origin))
angle_1 = int(np.random.normal(90, 10)) + rotation
angle_2 = int(np.random.normal(90, 10)) + rotation
quad_offset = 0
if quadrant == 0:
quad_offset = 180
if quadrant == 1:
quad_offset = 90
if quadrant == 3:
quad_offset += 270
angle_1 += quad_offset
angle_2 += quad_offset
mp_len = int(line_segment_len(dest, m))
amplitude_1 = from_range(mp_len // 2, mp_len)
amplitude_2 = from_range(mp_len // 2, mp_len)
c1 = point_pos(dest[0], dest[1], amplitude_1, angle_1)
c2 = point_pos(origin[0], origin[1], amplitude_2, angle_2)
if any(i for i in c1 + c2) < 0:
return generate_arc_controls(dest, origin, quadrant)
d_dest_c1 = line_segment_len(dest, c1)
d_dest_c2 = line_segment_len(dest, c2)
return [c1, c2] if d_dest_c1 > d_dest_c2 else [c2, c1]
def generate_figure(self):
BOT_L = 0
TOP_L = 1
TOP_R = 2
initial_position = (400, from_range(450, 750))
segments = []
min_segments_per_q = 2
max_segments_per_q = 4
quadrant_intersects = [(random.choice(range(50, 350)), 400),
(400, random.choice(range(50, 350))),
(random.choice(range(450, 750), 400), initial_position)]
avg_dist = 150 # used to give some control
dist_variance = 50 # over the points I later join
for quad in range(0,4):
seg_count = from_range(min_segments_per_q, max_segments_per_q)
x_offset = 0 if quad in [BOT_L, TOP_L] else 400
y_offset = 0 if quad in [TOP_L, TOP_R] else 400
origin = None
for j in range(0, seg_count):
# generate start and end points for each segment in the quadrant
q_points = self.generate_segment_positions(seg_count, x_offset, y_offset, avg_dist, dist_variance)
# set origin to the destination of previous segment
o = initial_position if origin is None else origin
# assign destination point; quadrant intersect for last segment of each quadrant
d = q_points[j] if j < seg_count - 1 else quadrant_intersects[quad]
# choose a segment type
s = random.choice(['arc', 'line'])
if s == 'line':
segments.append(['line', [d, o]])
if s == 'arc':
c = self.generate_arc_controls(d, o, quad)
segments.append(['arc', [c[0], c[1], d]])
origin = d
surf = aggdraw.Draw("RGBA", (800, 800), (255,255,255,255))
p_str = "M{0} {1}".format(*initial_position)
for s in segments:
if s[0] == 'line':
p_str += " L{0} {1}".format(*s[1][0])
if s[0] == 'arc':
pts = s[1][0] + s[1][2]
p_str += " Q {0} {1} {2} {3}".format(*pts)
sym = aggdraw.Symbol(p_str)
surf.symbol((0,0), sym, aggdraw.Pen((255,0,0), 1, 255))
return surf