Drawing—shapes
Objectives
- Be able to generate and display stimuli formed from a variety of shapes (lines, rectangles, circles).
Using psychopy, we can also easily draw a variety of shapes.
Lines
Perhaps the most simple “shape” stimulus is a line, which we can create in psychopy using Line
.
The two critical arguments to create a line are start
and end
, which are each two-item lists that specify the x and y coordinates of the start and end points of the line, respectively.
For example, to draw a line from the bottom left to the top right of our screen, we could do:
import psychopy.visual
import psychopy.event
win = psychopy.visual.Window(
size=[400, 400],
units="pix",
fullscr=False,
color=[1, 1, 1]
)
line = psychopy.visual.Line(
win=win,
units="pix",
lineColor=[-1, -1, -1]
)
line.start = [-200, -200]
line.end = [+200, +200]
line.draw()
win.flip()
psychopy.event.waitKeys()
win.close()
We can also change the colour of the line using lineColor
and its width using lineWidth
:
import psychopy.visual
import psychopy.event
win = psychopy.visual.Window(
size=[400, 400],
units="pix",
fullscr=False,
color=[1, 1, 1]
)
line = psychopy.visual.Line(
win=win,
units="pix",
lineColor=[-1, -1, -1]
)
line.start = [-200, -200]
line.end = [+200, +200]
line.lineColor = [-1, -1, 1]
line.lineWidth = 5
line.draw()
win.flip()
psychopy.event.waitKeys()
win.close()
Many visual illusions can be created using simple line arrangements. For example, by drawing a few lines we can create a Ponzo illusion, where the upper horizontal bar looks a bit bigger than the lower horizontal bar:
import psychopy.visual
import psychopy.event
win = psychopy.visual.Window(
size=[400, 400],
units="pix",
fullscr=False,
color=[1, 1, 1]
)
line = psychopy.visual.Line(
win=win,
units="pix",
lineColor=[-1, -1, -1]
)
bar_horiz_offset = 20
bar_vert_offset = 80
for bar_offset in [-1, +1]:
line.start = [-bar_horiz_offset, bar_vert_offset * bar_offset]
line.end = [+bar_horiz_offset, bar_vert_offset * bar_offset]
line.draw()
pers_far_horiz_offset = 150
pers_near_horiz_offset = 10
pers_vert_offset = 140
for pers_offset in [-1, +1]:
line.start = [pers_far_horiz_offset * pers_offset, -pers_vert_offset]
line.end = [pers_near_horiz_offset * pers_offset, +pers_vert_offset]
line.draw()
win.flip()
win.getMovieFrame()
psychopy.event.waitKeys()
win.close()
Rectangles
We can also create rectangles, using psychopy’s Rect
.
The two critical arguments are the height
and width
of the rectangle.
We can also set a fillColor
.
import psychopy.visual
import psychopy.event
win = psychopy.visual.Window(
size=[400, 400],
units="pix",
fullscr=False,
color=[1, 1, 1]
)
rect = psychopy.visual.Rect(
win=win,
units="pix",
width=200,
height=100,
fillColor=[1, -1, -1],
lineColor=[-1, -1, 1]
)
rect.draw()
win.flip()
psychopy.event.waitKeys()
win.close()
A class of visual stimuli that makes heavy use of rectangles are the so-called Mondrian patterns. In such a stimulus, a large number of rectangles of varying widths and heights are drawn at random positions—which ends up producing a pleasant variegated stimulus:
import random
import psychopy.visual
import psychopy.event
win = psychopy.visual.Window(
size=[400, 400],
units="pix",
fullscr=False,
color=[1, 1, 1]
)
rect = psychopy.visual.Rect(win=win, units="pix")
n_rect = 500
for i_rect in range(n_rect):
rect.width = random.uniform(10, 100)
rect.height = random.uniform(10, 100)
rect_color = random.uniform(-1, 1)
rect.fillColor = rect_color
rect.lineColor = rect_color
rect.pos = [
random.uniform(-200, 200),
random.uniform(-200, 200)
]
rect.draw()
win.flip()
psychopy.event.waitKeys()
win.close()
Circles
Circles can also be useful, and are created by psychopy’s Circle
.
The critical parameter for a circle is its radius
.
import psychopy.visual
import psychopy.event
win = psychopy.visual.Window(
size=[400, 400],
units="pix",
fullscr=False,
color=[1, 1, 1]
)
circle = psychopy.visual.Circle(
win=win,
units="pix",
radius=150,
fillColor=[0, 0, 0],
lineColor=[-1, -1, -1]
)
circle.draw()
win.flip()
psychopy.event.waitKeys()
win.close()
Notice though that the edges of the circle are not all that smooth. That is because the circle needs to be approximated with a set of points, and psychopy uses 32 by default. We can increase that a bit to give smoother circles (at the cost of more computation and storage requirements):
import psychopy.visual
import psychopy.event
win = psychopy.visual.Window(
size=[400, 400],
units="pix",
fullscr=False,
color=[1, 1, 1]
)
circle = psychopy.visual.Circle(
win=win,
units="pix",
radius=150,
fillColor=[0, 0, 0],
lineColor=[-1, -1, -1],
edges=128
)
circle.draw()
win.flip()
psychopy.event.waitKeys()
win.close()
That looks nicer. Keeping up with the tradition of this lesson, we can create a compelling size illusion (called the Ebbinghaus illusion) using a few circles:
import psychopy.visual
import psychopy.event
import psychopy.misc
win = psychopy.visual.Window(
size=[400, 400],
units="pix",
fullscr=False,
color=[1, 1, 1]
)
circle = psychopy.visual.Circle(
win=win,
units="pix",
fillColor=[-1] * 3,
lineColor=[-1] * 3,
edges=128
)
# 'test' circles
circle.radius = 12
test_offset = 100
for offset in [-1, +1]:
circle.pos = [test_offset * offset, 0]
circle.draw()
# 'surround' circles
surr_thetas = [0, 72, 144, 216, 288]
surr_r = 50
for i_surr in range(len(surr_thetas)):
[surr_pos_x, surr_pos_y] = psychopy.misc.pol2cart(
surr_thetas[i_surr],
surr_r
)
surr_pos_x = surr_pos_x + test_offset
circle.pos = [surr_pos_x, surr_pos_y]
circle.radius = 25
circle.draw()
win.flip()
win.getMovieFrame()
psychopy.event.waitKeys()
win.close()
Tip
Note the use of a new function in the above code, psychopy.misc.pol2cart
.
Often when creating visual stimuli it can be more intuitive to work in polar coordinates (polar angle, radius) than cartesian coordinates (x, y).
We can use this function to easily convert from polar to cartesian.