私は認知心理学の実験を書いています。いくつかのプロパティを任意に制御して落書きのような図形を生成する必要があります。
- Figure は決して自己交差できません
- 閉じなければならない
- 描画面/キャンバスの大部分を占める必要があります
- 一連のポイントとしてエクスポートできる必要があります
さらに、その複雑さをある程度制御する必要があります。
これまでの私の一般的なアプローチは、これを行うことでした:
- キャンバスを四分円に分割し、各軸に沿って交点を生成します。これにより、図形がすべての象限を通過するようにすることで、図形が領域の大部分を占めることが保証されます。
- 各象限に乱数のポイントを生成します。
- 直線か曲線のどちらかをランダムに選び、点を順番につないでいきます
これは機能しますが、自己交差をまったく制御できません。これを制御できる方法が愚かで力強いアプローチであることはすでにわかっています。コードを含めましたが、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