1

私は認知心理学の実験を書いています。いくつかのプロパティを任意に制御して落書きのような図形を生成する必要があります。

  • Figure は決して自己交差できません
  • 閉じなければならない
  • 描画面/キャンバスの大部分を占める必要があります
  • 一連のポイントとしてエクスポートできる必要があります

さらに、その複雑さをある程度制御する必要があります。

これまでの私の一般的なアプローチは、これを行うことでした:

  1. キャンバスを四分円に分割し、各軸に沿って交点を生成します。これにより、図形がすべての象限を通過するようにすることで、図形が領域の大部分を占めることが保証されます。
  2. 各象限に乱数のポイントを生成します。
  3. 直線か曲線のどちらかをランダムに選び、点を順番につないでいきます

これは機能しますが、自己交差をまったく制御できません。これを制御できる方法が愚かで力強いアプローチであることはすでにわかっています。コードを含めましたが、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
4

0 に答える 0