4

My code here works, but my current method is very inefficient and time consuming. I'm generating 4 Cartesian coordinates and appending them to a list. I'm then creating 4 Psychopy line objects (visual.Line) and assigning each object an x,y coordinate from my coordinates list (zdot_list). Currently, I'm creating 4 lines objects one after the other, and assigning an xy position to each 'start' parameter.

from psychopy import visual, core, event, sound
import random
import math

win = visual.Window([800,600],color=(0,0,0), colorSpace='rgb', rgb=None, allowGUI=True, monitor='testMonitor', units='deg', fullscr=True, screen=2)

# input polar, output cartesian coords
def pol_to_cart(distance, angle, x_origin=0, y_origin=0):

    x=distance*math.cos(math.radians(angle))
    y=distance*math.sin(math.radians(angle))

    return x +x_origin, y + y_origin


zdots = 4
zdot_list = []
j=(-8)

# generate 4 xy coordinates and append to list
for i in range(zdots):

    angle=0

    line_cart = pol_to_cart(j, angle)
    dotx = line_cart[0]
    doty = line_cart[1]
    zdot_list.append([dotx, doty])

    j += .2

# generate 4 lines, add xy coordinate (from list^) to 'start' argument of each
linea = visual.Line(win, start=(zdot_list[0]), end=(4,0), lineColor="white")
lineb = visual.Line(win, start=(zdot_list[1]), end=(4,0), lineColor="white")
linec = visual.Line(win, start=(zdot_list[2]), end=(4,0), lineColor="white")
lined = visual.Line(win, start=(zdot_list[3]), end=(4,0), lineColor="white")

lines = [linea, lineb, linec, lined]

# display lines
for line in lines:
    line.draw()
    win.flip()
    core.wait(3)
    win.close()

Is there a more efficient way to create line (or any shape) objects using a loop? I'd like to auto-generate the objects I need, adding my xy coordinate to each objects 'start' argument respectively. There are only 4 line objects in the image, but in reality I will need 80+, each with a different xy start coordinate.

Cheers, Jon

anatol
  • 791
  • 9
  • 16
Beatdown
  • 187
  • 2
  • 7
  • 20

2 Answers2

3

You can try and explore Polygon from visual module. Sample of Polygon usage below

from psychopy import visual, core, event, sound
win = visual.Window([680,480],color=(0,0,0), colorSpace='rgb', rgb=None, allowGUI=True, monitor='testMonitor', units='deg', fullscr=False, screen=2)
pgon = visual.Polygon(win, edges=4)
pgon.draw()
win.flip()

You can explore vertices and other options by going through psychophy documentation.

An optimisation to your code would be:

zdots=40
zdot_list = []

for i in range(zdots):
    angle=0
    line_cart = pol_to_cart(j, angle)
    zdot_list.append(visual.Line(win, start=([line_cart[0], line_cart[1]]), end=(4,0), lineColor="white"))
    j += .2

for i in zdot_list:
    i.draw()

win.flip()
core.wait(3)
win.close()
Beatdown
  • 187
  • 2
  • 7
  • 20
be_good_do_good
  • 4,311
  • 3
  • 28
  • 42
  • Your optimisation shows one line at a time, I need all shown together. – Beatdown Jun 24 '17 at 18:50
  • @Jon optimisation changed to display all at once. – be_good_do_good Jun 25 '17 at 04:00
  • Is there a way so that I don't need to draw them during the loop itself? If I draw these outside of the loop now, I only see one line (probably the last iteration of the loop). I need more control of when I draw these lines. – Beatdown Jun 25 '17 at 12:00
  • Ah I got it, I'm using your solution, but appending the visual.Line objects to a list on every iteration of the loop, works nicely. Thank you. I edited your answer, if you accept the edit I will green tick it. – Beatdown Jun 25 '17 at 12:19
2

With 4 lines the suggested solution is fine. With more lines in the array you should use ElementArrayStim for more efficient drawing of large numbers of objects. This was originally designed for drawing bitmap textures, but if you set the size parameter to be long and thin, and set texture to nothing, then you effectively get a line. See the code here for an example:

https://github.com/psychopy/psychopy/issues/1350

So, for scaling up to 80+ lines you should go that route.

Jon
  • 1,198
  • 7
  • 8
  • Using ElementArrayStim, how would I create a start and endpoint for the line? These options aren't available in the class params. – Beatdown Jul 03 '17 at 14:25