 #### Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Supported by

# [solved] Pip & Pop concentric presentation

edited February 2014

Hello,

First of all, thank you for this great resource you are making available to us.

My question: I would like to adapt the script you published for the Pip & Pop task so that:

• the stimuli appear on a specific number of concentric circles (I'm thinking 3 or 4) instead of in a rectangular grid.
• I can store the location where the target appeared in each trial as one or two variables (preferably angle + distance to center)

My understanding of Python is very limited. I guessed that the piece of script below (your original script) handles building the stimulus. I thought maybe the RadialStim object (http://www.psychopy.org/epydoc/psychopy.visual.RadialStim-class.html) could do the job, however I am at a loss as to how I should implement it in this script or if it's even the way to go.

If there is any way you can point me in the right direction I would appreciate it very much!

Cheers,
Raquel

``````# Generate a list of orientations and positions
l_stim = [] # List of all stimuli
for i in range(n_dist):

# Pick an orientation
if i == 0:
ori = ori_target
else:
ori = choice(ori_dist)

# Pick a color:
col = choice(colors)

# Pick an available position on the screen
redo = True
while redo:
x = randint(xmin, xmax)
y = randint(ymin, ymax)
redo = False
for _stim,_x,_y,_ori,_col in l_stim+[fixdot]:
d = sqrt( (_x-x)**2 + (_y-y)**2 )
if d < min_d:
redo = True
break

# Generate and save the stimulus
stim = PatchStim(self.experiment.window, tex=None, mask="gauss", pos=(x,y), size=size, ori=ori, color=col)
l_stim.append( (stim,x,y,ori,col) )
``````

• edited 5:49PM

Hi Raquel,

The PsychoPy `RadialStim` class is used to display retinotopic-mapping-like stimuli. In your case, what you need to do is pick your stimulus coordinates so that they lie on an imaginary circle. This is actually quite easy, and you see it demonstrated in the script below, which is snippet from the script in the Pip & Pop example. As you can see, you need to change very little relative to the original.

Basically, you use the stimulus number (`i`) to determine the angle. Because the first stimulus is always the target, you also need to rotate the stimulus display in its entirety by a random `offset`, otherwise the target will always appear at the same location. Does that make sense?

If you have no experience with Python, I would recommend walking through one of the tutorials. You will need some knowledge if you want to create these types of dynamic stimulus displays!

Cheers!
Sebastiaan

``````# (...)
# This script is not standalone, but a snippet from the `dynamic_search_display`
# script in the Pip & Pop example

from math import cos, sin, pi
from random import randint

# An offset, which determines the position of the target. I.e. how much the
# entire circle of stimuli is rotated.
offset = randint(1, n_dist)

# Generate a list of orientations and positions
l_stim = [] # List of all stimuli
for i in range(n_dist):

# Pick an orientation
if i == 0:
ori = ori_target
else:
ori = choice(ori_dist)

# Pick a color:
col = choice(colors)

# Use a radius and angle to determine the position of the stimulus
a = 2*pi*(i+offset) / n_dist # Angle
x = cos(a) * r
y = sin(a) * r

# Generate and save the stimulus
stim = PatchStim(self.experiment.window, tex=None, mask="gauss", \
pos=(x,y), size=size, ori=ori, color=col)
l_stim.append( (stim,x,y,ori,col) )

# (...)
``````
• edited 5:49PM

This was exactly what I needed, many thanks!

Cheers,
Raquel

• edited February 2014

Hi Sebastiaan,

Thanks again for your help, I have an experiment now that does ALMOST exactly what I want . I was hoping to be able to share the code for a working experiment, instead I'm asking another question..

I've adapted your code so that the stimuli appear on three concentric circles. The target can appear on any one of those circles. It works perfectly, except for when the target appears on the middle circle. In that case it jumps to the other side of the circle where it overlaps with the distractor at that same spot. I can't for the life of me figure out what I did wrong, maybe you can see my mistake?

I'm only using set sizes 24 and 48. I've added the variable "eccent" which stands for eccentricity and determines value r for radius (100, 175 or 250).

Cheers,
Raquel

``````# (...)
# This script is not standalone, but a snippet from the `dynamic_search_display`
# script in the Pip & Pop example

# functions needed for radial stimuli generation
from math import cos, sin, pi
from random import randint

# An offset, which determines the position of the target. I.e. how much the
# entire circle of stimuli is rotated. Depends on if the target appears on the
# inner, middle or outer circle
offset_inner = randint(1, n_dist/6)
offset_middle = randint(1, n_dist/3)
offset_outer = randint(1, n_dist/2)

# Generate a list of orientations and positions
l_stim = [] # List of all stimuli

# Lists of remaining values of r, after r has been assigned to the target
#(by the variable "eccent"). e.g. if eccent is 100 (inner circle) and
# n_dist is 48, only 7 spots remain on the inner circle for distractors.

radii_inner_48 = [100, 100, 100, 100, 100, 100, 100, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250]
radii_middle_48 = [100, 100, 100, 100, 100, 100, 100, 100, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250]
radii_outer_48 = [100, 100, 100, 100, 100, 100, 100, 100, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250]
radii_inner_24 = [100, 100, 100, 175, 175, 175, 175, 175, 175, 175, 175, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250]
radii_middle_24 = [100, 100, 100, 100, 175, 175, 175, 175, 175, 175, 175, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250]
radii_outer_24 = [100, 100, 100, 100, 175, 175, 175, 175, 175, 175, 175, 175, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250]

#Assigning a value r for radius and a value a for angle to the target and distractors:

for i in range(n_dist):

# Pick an orientation
if i == 0:
ori = ori_target
else:
ori = choice(ori_dist)

# Pick a color:
col = choice(colors)

if i == 0:
r = eccent
else:
if n_dist == 48:
if eccent == 100:
elif eccent == 175:
else:
else:
if eccent == 100:
elif eccent == 175:
else:

# Pick an angle, the number added to i represents the offset, which
# determines the position of the target. I.e. how much the
# entire circle of stimuli is rotated.
if r == 100:
a = 2*pi*(i+offset_inner) / (n_dist/6)
elif r == 175:
a = 2*pi*(i+offset_middle) / (n_dist/3)
else:
a = 2*pi*(i+offset_outer) / (n_dist/2)

# Use radius r and angle a to determine the position of the stimulus

x = cos(a) * r
y = sin(a) * r

# Generate and save the stimulus
stim = PatchStim(self.experiment.window, tex=None, mask="gauss", pos=(x,y), size=size, ori=ori, color=col)
l_stim.append( (stim,x,y,ori,col) )

#(...)
``````
• edited 5:49PM

Hi Raquel,

One (probably the) thing that goes wrong is the division. Let's take a look at this:

`````` a = 2*pi*(i+offset_inner) / (n_dist/6)
``````

What you are doing is dividing by the division of two `int`s: (`n_dist/6`). The problem here is that `int` divisions always return an `int`, and never a `float` (in Python 2, which is used by OpenSesame). Or, phrased differently, decimals are lost.

``````print 0/3 # will print 0
print 1/3 # will print 0
print 2/3 # will print 0
print 3/3 # will print 1
print 4/3 # will print 1
``````

What you need to do is make sure that one of the numbers is a `float`:

``````print 1.0/3 # will print 0.333 ...
print 1/3.0 # will print 0.333 ...
``````

For example:

`````` a = 2*pi*(i+offset_inner) / (float(n_dist)/6)
``````

Does that solve the issue?

Cheers,
Sebastiaan

• edited February 2014

Hi Sebastiaan,

Thanks for your comment! I used floats as you suggested, but that didn't solve the issue. In the end the problem was the order of the items in the lists defining the radii for the distracter items. If anyone would like to know about the reason for the problem just let me know. Below is a version of the script that seems to be working.

Cheers,
Raquel

``````# (...)
# This script is not standalone, but a snippet from the `dynamic_search_display`
# script in the Pip & Pop example modified to present stimuli on 3 concentric circles.

# functions needed for radial stimuli generation
from math import cos, sin, pi
from random import randint

# An offset, which determines the position of the target. I.e. how much the
# entire circle of stimuli is rotated. Depends on if the target appears on the inner, middle or outer circle
offset_inner = randint(1, float(n_dist)/6)
offset_middle = randint(1, float(n_dist)/3)
offset_outer = randint(1, float(n_dist)/2)

# Generate a list of orientations and positions
l_stim = [] # List of all stimuli

# Lists of remaining values of r, after r has been assigned to the target (by the variable "eccent"). e.g. if eccent is 100 (inner circle) and
# n_dist is 48, only 7 spots remain on the inner circle for distractors.

radii_inner_48 = [100, 100, 100, 100, 100, 100, 100, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250]
radii_middle_48 = [175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 100, 100, 100, 100, 100, 100, 100, 100, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250]
radii_outer_48 = [250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 100, 100, 100, 100, 100, 100, 100, 100, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175]
radii_inner_24 = [100, 100, 100, 175, 175, 175, 175, 175, 175, 175, 175, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250]
radii_middle_24 = [175, 175, 175, 175, 175, 175, 175, 100, 100, 100, 100, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250]
radii_outer_24 = [250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 100, 100, 100, 100, 175, 175, 175, 175, 175, 175, 175, 175]

#Assigning a value r for radius and a value a for angle to the target and distractors:

for i in range(n_dist):

# Pick an orientation
if i == 0:
ori = ori_target
else:
ori = choice(ori_dist)

# Pick a color:
col = choice(colors)

if i == 0:
r = eccent
else:
if n_dist == 48:
if eccent == 100:
elif eccent == 175:
else:
else:
if eccent == 100:
elif eccent == 175:
else:

# Pick an angle, the number added to i represents the offset, which determines the position of the target. I.e. how much the
# entire circle of stimuli is rotated.
if r == 100:
a = 2*pi*(i+offset_inner) / (float(n_dist)/6)
elif r == 175:
a = 2*pi*(i+offset_middle) / (float(n_dist)/3)
else:
a = 2*pi*(i+offset_outer) / (float(n_dist)/2)

# Use radius r and angle a to determine the position of the stimulus

x = cos(a) * r
y = sin(a) * r

# Generate and save the stimulus
stim = PatchStim(self.experiment.window, tex=None, mask="gauss", pos=(x,y), size=size, ori=ori, color=col)
l_stim.append( (stim,x,y,ori,col) )

#...
``````
• edited 5:49PM

Good to hear you got it working, and thanks for sharing the script!