Creating a 37-item circular visual search display with OpenSesame inline scripts

In this post I am going to explain how you can create a visual search display for a compound visual search task with an additional singleton that can either be singled out by color or orientation. The display has multiple items arranged in concentric circles. At the bottom there is the full code included in a working OpenSesame example.

figure1_display
This is how the final result looks like. There are 37 bars in the display including one search target (slightly tilted) and one additional singleton (horizontal).
figure2_overview
Here is the overview over the experiment.

There is nothing really special about the setup of the experimental structure in OpenSesame. The two scripts of interest are general_functions and search_display.

In search_display, I defined everything that is important to set up the display. This is called and executed on every trial. The script general_functions only includes the functions to actually draw each stimulus. The function is called from search_display for every item individually.

General idea

We want the search display to be arranged in concentric circles, so what we do is create three lists, one for each circle,  which we then fill with objects. These objects contain the construction manual (i.e. size, color, orientation, etc) for the items in the search display

The inner circle has 6 items that are always nontargets (in this example), the outer circler has 18 items which are also always nontargets. These lists can be filled easily because we basically need to put in the same thing 6 respectively 18 times. The middle circle contains 12 items, one of which is the search target and one may be a distractor.

When we filled the lists with all the objects, we wil go on and loop through the lists to actually draw the items circle by circle and item by item until the whole search display is drawn.

Importantly, everything needs to be put into the prepare phase of OpenSesame because creating and drawing the stimuli is computation intensive and might slow down the experiment on slower machines. We only need one command in the run phase, which we will see later.

Below, I often use the function self.get(“VariableName“). You can replace this with var.VariableName. It’s an artifact from earlier version of OpenSesame.

Setting basic properties

First, we need to import necessary libraries which we should put in general_functions.

import math
import random
from random import randrange

Next, we start working within search_display. We set the basic colors for our stimuli and create the three empty lists.

# Determine the properties of the
# target, distractor and non-targets

target_color = "#FC0015"
nontarget_color = "#00a0a6"
distractor_color = "#FC0015"

# Create three lists filled with the target, the
# distractor (if present) and a number of
# non-targets, which depends on the display size.
# The lists represent the three circles.
# Target and distractor can only appear on the middle circle.

# Create an empty list of stimuli
stimuli_circle1 = [] 
stimuli_circle2 = [] # circle with target and distractor
stimuli_circle3 = []

Then we need to define the angles where stuff can appear on the circle. Usually you want to have all items equidistantly arranged across the whole extent of the circle.

# Set the angles at which targets can appear
# assuming a circle with 12 possible positions
angles = [0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330]

Remember that stimuli_circle1 and stimuli_circle3 will only contain nontargets so we will deal with them later.

Defining target and distractor

First, we specify the location of the search target. You can have a set locaiton or do it randomly, like me.

# Select random angle for the target and remove it from the lsit
target_location = angles.pop(randrange(0,len(angles)))

With randrange, we select a random number within the specified range. The function pop returns an item of the list which is now saved in target_location and at the same time removes the item from the list. This is good, because we don’t want to have multiple items at the same locations.

In these types of compound searches, you want the target to have more than one “identity” so that not the same button is the correct answer in every trial (duh!). In my case, I change the orientation of the target so it either looks like an i or an !.

# Based on target identitiy (i or !), randomly pick a target orientation and add the target to the list of stimuli
if self.get("target_identity") == "i":
	target_orient = random.choice( (12, 348) )
else:
	target_orient = random.choice( (192, 162) )

Then we add our target item for the list of items (it’s the first in the list, yay!).

stimuli_circle2.append( (target_color, target_orient, target_location) )

The distractor is slightly more complex because we first need to check whether it is actually present in the current display and if yes, what type the distractor is. Afterwards, we can generate its location and append it to the list stimuli_circle2.

# If a distractor is present in the display,
# determine its orientation and color based on the distractor type
if self.get("distractor") == "present":
	if self.get("distractor_type") == "color":
		distractor_color = "#FC0015" 			# red distractor
		distractor_orient = random.choice( (0,180) )	
		
	if self.get("distractor_type") == "orient":
		distractor_orient = random.choice( (90,270) ) 	# horizontal distractor
	
	# Set random distracotr location
	distractor_location = angles.pop(randrange(0,len(angles)))

	# Append the distractor to the list of display items
	stimuli_circle2.append( (distractor_color, distractor_orient, distractor_location) )

Adding the nontargets

The idea for the inner and outer circle is simple. We create a loop that generates a random orientation for each nontarget and add it to the list. It looks like below for the inner circle.

# Add the remaining nontarget stimuli to the list - CIRCLE 1
for i in range(6 - int(len(stimuli_circle1))):
	nontarget_orient = random.choice((0,180))
	stimuli_circle1.append( (nontarget_color, nontarget_orient) )

For the outer circle, you might have guessed it, the 6 needs to be exchanged for an 18. What we do here is to say: as long as our list of object hasn’t reached the total number of objects we want (i.e. 6), append more objects! For the middle circle, we need to add information about the location of the objects, because one or two locations are already occupied with a target plus possibly distractor.

# Add the remaining nontarget stimuli to the list - CIRCLE 2
for i in range(12 - len(stimuli_circle2)):
	nontarget_orient = random.choice((0,180))
	nontarget_location = angles.pop(randrange(0,len(angles)))
	stimuli_circle2.append( (nontarget_color, nontarget_orient, nontarget_location) )

Like we did with the target and distractor, we get our remaining object locations by popping them out of the angles list. We have three lists filled with all our items now and can start to create the canvas where we later draw the items on.

Creating the canvas and setting circle properties

Creating a canvas is simple.

# Create a new offline canvas
self.c = self.offline_canvas()

# We want the stimuli to have some body
self.c.set_penwidth(1)

We now need to decide how big our circles are gonna be. The sizes can be specified relatively or absolutely. Since I wanted to make sure that they look the same on all screens, I specified absolute pixel values. This is something you just need to try out or calculate from the visual angle you want to achieve.

# Setting circle properties
radius_circle1 = 341 * 0.3333 # Distance of the inner circle from center
radius_circle2 = 341 * 0.6667
radius_circle3 = 341
line_length = 76*0.5 # Size of the lines in the shapes
line_width = 14

# The angular separation of the stimuli depends on the number of items
angular_separation_circle1 = 360.0 / 6
angular_separation_circle2 = 360.0 / 12
angular_separation_circle3 = 360.0 / 18

# The first stimulis is drawn at angle 270 (12 clock position)
angle = 270

Above we defined the radius of each circle, the length and width of our stimuli as well as the angular separation of the items in the three circles. Importantly, the angle attribute specifies where we want the items to be drawn on the circle. The first item will be drawn at 270° which is the 12 o’clock position (I don’t know why).

Drawing all stimuli

The easiest thing is the center. There is one nontarget in the middle. We need to determine its location (i.e. the center of the screen) and then draw it with the correct attributes.

# Determine coordinates of center nontarget
x = self.get("width") / 2
y = self.get("height") / 2

# Set the color of the stimulus
self.c.set_fgcolor(nontarget_color)

# draw the center nontarget
draw_asp_stimulus(x, y, line_width, line_length, nontarget_orient, self.c)

The function draw_asp_stimulus draws the actual stimulus on the canvas and will be discussed later. After drawing the central nontarget we start drawing our items from the inner to the outer circle.

# Walk through all the stimuli - CIRCLE 1 - inner circle
for color, orient in stimuli_circle1:
	
	# Determine the coordinates of the stimulus
	x = self.get("width") / 2 + radius_circle1 * math.cos(math.radians(angle))
	y = self.get("height") / 2 + radius_circle1 * math.sin(math.radians(angle))

	# Set the color of the stimulus
	self.c.set_fgcolor(color)
	
	# Draw special ASP stimulus which is an iterrupted bar looking like an i or bang
	draw_asp_stimulus(x, y, line_width, line_length, orient, self.c)
	
	# Make sure the next stimulus is drawn at a different location
	angle += angular_separation_circle1

For each item in the stimuli_circle1 list, we determine the coordinates by using some fancy math based on the angle, set the foreground color and call the draw function. Then we add the angular separation to the angle, so that the next item will be drawn at the next position on the circle. This works similarly for all circles.

Actually drawing one stimulus

One item contains of one large turquoise rectangle and one small black rectangle which is either at the top or bottom of the large rectangle. The function draw_asp_stimulus takes the arguments x,y position, width, length, orientation and the canvas. The x,y position represents the center of mass of the object, not some corner! This means what we first need to do is calculate the x,y positions four corners.

def draw_asp_stimulus(x, y, line_width, line_length, orientation, c):

	# OUTER RECTANGLE
	# set corners
	x1 = x - line_width/2
	y1 = y - line_length
	x2 = x + line_width/2
	y2 = y - line_length
	x3 = x + line_width/2
	y3 = y + line_length
	x4 = x - line_width/2
	y4 = y + line_length

Since not all our items have the same orientation (especially target and distractor), we now need to rotate all the corner points. What we do is a point rotation around a central pivot point (the center of mass) by the orientation angle.

	# polygon rotation
	p1 = rotate_point(x, y, orientation, x1, y1)
	p2 = rotate_point(x, y, orientation, x2, y2)
	p3 = rotate_point(x, y, orientation, x3, y3)
	p4 = rotate_point(x, y, orientation, x4, y4)

Unfortunately, the rotate_point function, doesn’t really exist, so we need to create our own!

def rotate_point(cx, cy, angle, x, y):
	s = math.sin(math.radians(angle))
	c = math.cos(math.radians(angle))
	
	x = x - cx
	y = y - cy
	
	xnew = x * c - y * s
	ynew = x * s + y * c
	
	x = xnew + cx
	y = ynew + cy
	return [x,y]

I will spare you the mathematical details of the point rotation. We now created and rotated the corner points from the outer rectangle, we need to do the same with the inner rectangle.

# INNER RECTANGLE
	# set corners
	cx1 = x - line_width/2
	cy1 = y - line_length + line_width
	cx2 = x + line_width/2
	cy2 = y - line_length + line_width
	cx3 = x + line_width/2
	cy3 = y - line_length + line_width + line_width
	cx4 = x - line_width/2
	cy4 = y - line_length + line_width + line_width

	#point rotation
	cp1 = rotate_point(x, y, orientation, cx1, cy1)
	cp2 = rotate_point(x, y, orientation, cx2, cy2)
	cp3 = rotate_point(x, y, orientation, cx3, cy3)
	cp4 = rotate_point(x, y, orientation, cx4, cy4)

Lastly, we need to draw our rectangles with the polygon function on our canvas.

self.c.polygon([(p1[0], p1[1]),(p2[0], p2[1]), (p3[0], p3[1]), (p4[0], p4[1])], fill=True)
	self.c.set_fgcolor("black")
	self.c.polygon([(cp1[0], cp1[1]),(cp2[0], cp2[1]), (cp3[0], cp3[1]), (cp4[0], cp4[1])], fill=True)

We now created the canvas full of juicy objects, the only thing left to do is show it at runtime. The following command needs to be included in the run phase of the search_display.

# Show the canvas!
self.c.show()

Tadaa! Wasn’t so hard was it? If you have any questions, feel free to ask me at any time. I am also happy about suggestions for improving the code. Please comment or write me an email.

Below you can find the whole OpenSesame code for this experiment but you can also download it here.

---
API: 2
OpenSesame: 3.1.3
Platform: posix
---
set width 1024
set uniform_coordinates no
set transparent_variables no
set title "Marian Visual Search"
set synth_backend legacy
set subject_parity even
set subject_nr 0
set start experiment
set sound_sample_size -16
set sound_freq 48000
set sound_channels 2
set sound_buf_size 1024
set sampler_backend legacy
set round_decimals 2
set mouse_backend psycho
set keyboard_backend psycho
set height 768
set fullscreen no
set form_clicks no
set foreground white
set font_underline no
set font_size 28
set font_italic no
set font_family sans
set font_bold no
set experiment_path "/Users/mariansauter/Dropbox/PhD/Blog/Visual Search Display/MS-ASP-LT-1-Chronicles"
set disable_garbage_collection yes
set description "A probability cuing experiment"
set coordinates relative
set compensation 0
set color_backend psycho
set clock_backend psycho
set canvas_backend psycho
set bidi no
set background black

define advanced_delay advanced_delay
	set jitter_mode Uniform
	set jitter 200
	set duration 900
	set description "Waits for a specified duration"

define loop block_loop
	set source_file ""
	set source table
	set repeat 30
	set order random
	set item trial_sequence
	set description "A block of trials"
	set cycles 4
	set continuous no
	set column_order "distractor;circles;inner_circle_size;mid_circle_size;outer_circle_size;target_identity;distractor_type"
	set break_if_on_first yes
	set break_if never
	setcycle 0 distractor_type orient
	setcycle 0 target_identity i
	setcycle 0 distractor present
	setcycle 1 distractor_type orient
	setcycle 1 target_identity bang
	setcycle 1 distractor present
	setcycle 2 distractor_type orient
	setcycle 2 target_identity i
	setcycle 2 distractor absent
	setcycle 3 distractor_type orient
	setcycle 3 target_identity bang
	setcycle 3 distractor absent
	run trial_sequence

define sequence block_sequence
	set flush_keyboard yes
	set description "An instruction screen, followed by a block of trials and feedback"
	run reset_feedback always
	run block_loop always
	run feedback always

define inline_script each_block
	set description "Executes Python code"
	___run__
	current_block = current_block + 1;
	
	self.experiment.set("block_count", current_block)
	__end__
	set _prepare ""

define sketchpad error_display
	set reset_variables no
	set duration 495
	set description "Displays stimuli"
	draw textline center=1 color=white font_bold=no font_family=mono font_italic=no font_size=18 html=no show_if=always text=Error x=0 y=0 z_index=0

define sequence experiment
	set flush_keyboard yes
	set description "The main experimental sequence"
	run general_functions always
	run instruction_form always
	run experimental_loop always
	run goodbye always

define loop experimental_loop
	set source_file ""
	set source table
	set skip 0
	set repeat 12
	set order sequential
	set item block_sequence
	set description "Run a number of experimental blocks"
	set cycles 1
	set continuous no
	set column_order "practice;condition"
	set break_if_on_first yes
	set break_if never
	setcycle 0 practice no
	setcycle 0 condition top
	run block_sequence

define feedback feedback
	set reset_variables yes
	set duration keypress
	set description "Provides feedback to the participant"
	draw textline center=1 color=white font_bold=no font_family=mono font_italic=no font_size=18 html=yes show_if=always text="Your average response time was [avg_rt]ms" x=0 y=-128 z_index=0
	draw textline center=1 color=white font_bold=no font_family=mono font_italic=no font_size=18 html=yes show_if=always text="Your accuracy was [acc]%" x=0 y=-64 z_index=0
	draw textline center=1 color=white font_bold=no font_family=mono font_italic=no font_size=18 html=yes show_if=always text="Press any key to continue ..." x=0 y=64 z_index=0
	draw textline center=1 color=white font_bold=no font_family=mono font_italic=no font_size=18 html=yes show_if=always text="This was block [block_count] of 12" x=0 y=-224 z_index=0

define fixation_dot fixation_dot
	set y 0
	set x 0
	set style cross
	set penwidth 3
	set foreground white
	set duration 0
	set description "Presents a central fixation dot with a choice of various styles"
	set background black

define inline_script general_functions
	set description "Executes Python code"
	___run__
	current_block = 0
	
	import math
	import random
	from random import randrange
	
	# function rotate_point rotates a point x,y at a given angle around a pivot point cx,cy
	def rotate_point(cx, cy, angle, x, y):
		s = math.sin(math.radians(angle))
		c = math.cos(math.radians(angle))
		
		x = x - cx
		y = y - cy
		
		xnew = x * c - y * s
		ynew = x * s + y * c
		
		x = xnew + cx
		y = ynew + cy
		return [x,y]
	
	# draws a rotated stimulus for the ASP paradigm
	# takes center x, y, stimulus width and height and its orientation
	def draw_asp_stimulus(x, y, line_width, line_length, orientation, c):
	
		# OUTER RECTANGLE
		# set corners
		x1 = x - line_width/2
		y1 = y - line_length
		x2 = x + line_width/2
		y2 = y - line_length
		x3 = x + line_width/2
		y3 = y + line_length
		x4 = x - line_width/2
		y4 = y + line_length
		
		# point rotation
		p1 = rotate_point(x, y, orientation, x1, y1)
		p2 = rotate_point(x, y, orientation, x2, y2)
		p3 = rotate_point(x, y, orientation, x3, y3)
		p4 = rotate_point(x, y, orientation, x4, y4)
		
		# INNER RECTANGLE
		# set corners
		cx1 = x - line_width/2
		cy1 = y - line_length + line_width
		cx2 = x + line_width/2
		cy2 = y - line_length + line_width
		cx3 = x + line_width/2
		cy3 = y - line_length + line_width + line_width
		cx4 = x - line_width/2
		cy4 = y - line_length + line_width + line_width
	
		#point rotation
		cp1 = rotate_point(x, y, orientation, cx1, cy1)
		cp2 = rotate_point(x, y, orientation, cx2, cy2)
		cp3 = rotate_point(x, y, orientation, cx3, cy3)
		cp4 = rotate_point(x, y, orientation, cx4, cy4)
	
		self.c.polygon([(p1[0], p1[1]),(p2[0], p2[1]), (p3[0], p3[1]), (p4[0], p4[1])], fill=True)
		self.c.set_fgcolor("black")
		self.c.polygon([(cp1[0], cp1[1]),(cp2[0], cp2[1]), (cp3[0], cp3[1]), (cp4[0], cp4[1])], fill=True)
	__end__
	set _prepare ""

define sketchpad goodbye
	set start_response_interval no
	set reset_variables no
	set duration keypress
	set description "Say goodbye!"
	draw textline center=1 color=white font_bold=no font_family=mono font_italic=no font_size=18 html=yes show_if=always text="The experiment is finished!" x=0 y=-64 z_index=0
	draw textline center=1 color=white font_bold=no font_family=mono font_italic=no font_size=18 html=yes show_if=always text="Press any key to exit ..." x=0 y=64 z_index=0

define sketchpad green_fixdot
	set start_response_interval no
	set reset_variables no
	set duration 500
	set description "Displays stimuli"
	draw fixdot color=green show_if=always style="medium-cross" x=0 y=0 z_index=0

define sketchpad instruction_form
	set start_response_interval no
	set reset_variables no
	set duration keypress
	set description "Present instruction for the form condition"
	draw textline center=1 color=white font_bold=no font_family=sans font_italic=no font_size=18 html=yes show_if=always text="When you have read and understood the instructions, <br />you can start the experiment by pressing any key." x=0 y=0 z_index=0

define keyboard_response keyboard_response
	set timeout infinite
	set flush yes
	set duration keypress
	set description "Collects keyboard responses"
	set allowed_responses "y;m"

define logger logger
	set use_quotes yes
	set ignore_missing yes
	set description "Logs experimental data"
	set auto_log no
	log fixation_interval
	log response
	log response_time
	log correct
	log correct_keyboard_response
	log average_response_time
	log avg_rt
	log accuracy
	log acc
	log count_experimental_loop
	log practice
	log condition
	log repeat
	log count_block_loop
	log target_identity
	log distractor_type
	log mid_circle_size
	log distractor
	log inner_circle_size
	log outer_circle_size
	log foreground
	log background
	log target_location
	log target_pos
	log target_color
	log target_orient
	log distractor_pos
	log distractor_location
	log distractor_color
	log distractor_orient
	log correct_response
	log count_trial_sequence
	log block_count
	log height
	log subject_nr
	log width
	log count_block_sequence
	log response_time_instruction_form

define sketchpad red_fixdot
	set start_response_interval no
	set reset_variables no
	set duration 500
	set description "Displays stimuli"
	draw fixdot color=red show_if=always style="medium-cross" x=0 y=0 z_index=0

define reset_feedback reset_feedback
	set description "Resets the feedback variables, such as 'avg_rt' and 'acc'"

define inline_script search_display
	set description "The search display"
	___run__
	# Show the canvas!
	self.c.show()
	__end__
	___prepare__
	
	# Determine the properties of the
	# target, distractor and non-targets
	
	target_color = "#FC0015"
	nontarget_color = "#00a0a6"
	distractor_color = "#FC0015"
	
	# Create three lists filled with the target, the
	# distractor (if present) and a number of
	# non-targets, which depends on the display size.
	# The lists represent the three circles.
	# Target and distractor can only appear on the middle circle.
	
	# Create an empty list of stimuli
	stimuli_circle1 = [] 
	stimuli_circle2 = [] # circle with target and distractor
	stimuli_circle3 = []
	
	# Set the angles at which targets can appear
	# assuming a circle with 12 possible positions
	angles = [0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330]
	
	# Select random angle for the target and remove it from the lsit
	target_location = angles.pop(randrange(0,len(angles)))
	
	# Based on target identitiy (i or !), randomly pick a target orientation and add the target to the list of stimuli
	if self.get("target_identity") == "i":
		target_orient = random.choice( (12, 348) )
	else:
		target_orient = random.choice( (192, 162) )
	
	stimuli_circle2.append( (target_color, target_orient, target_location) )
	
	
	# If a distractor is present in the display,
	# determine its orientation and color based on the distractor type
	if self.get("distractor") == "present":
		if self.get("distractor_type") == "color":
			distractor_color = "#FC0015" 					# red distractor
			distractor_orient = random.choice( (0,180) )	
			
		if self.get("distractor_type") == "orient":
			distractor_orient = random.choice( (90,270) ) 	# horizontal distractor
		
		# Set random distracotr location
		distractor_location = angles.pop(randrange(0,len(angles)))
	
		# Append the distractor to the list of display items
		stimuli_circle2.append( (distractor_color, distractor_orient, distractor_location) )
		
	# determine the list of locations to be filled with nontargets and shuffle them (i dont know why I do that)
	random.shuffle(angles)
	
	# Add the remaining nontarget stimuli to the list - CIRCLE 1
	for i in range(6 - int(len(stimuli_circle1))):
		nontarget_orient = random.choice((0,180))
		stimuli_circle1.append( (nontarget_color, nontarget_orient) )
	
	# Add the remaining nontarget stimuli to the list - CIRCLE 2
	for i in range(12 - len(stimuli_circle2)):
		nontarget_orient = random.choice((0,180))
		nontarget_location = angles.pop(randrange(0,len(angles)))
		stimuli_circle2.append( (nontarget_color, nontarget_orient, nontarget_location) )
	
	# Add the remaining nontarget stimuli to the list - CIRCLE 3
	for i in range(18 - len(stimuli_circle3)):
		nontarget_orient = random.choice((0,180))
		stimuli_circle3.append( (nontarget_color, nontarget_orient) )
	
	#
	# Set correct_repsonse dpending on condition
	# 'correct_response' is a special variable that, if it exists,
	# is interpreted as the expected response by the
	# keyboard_response item
	#
	
	if self.get("target_identity") == "i":
		self.experiment.set("correct_response", "m")
	else:
		self.experiment.set("correct_response", "y")
	
	###########################################################
	# Create an offline canvas containing all off the stimuli
	# and a fixation dot
	###########################################################
		
	# Create a new offline canvas
	self.c = self.offline_canvas()
	
	# Setting circle properties
	radius_circle1 = 341 * 0.3333 # Distance of the inner circle from center
	radius_circle2 = 341 * 0.6667
	radius_circle3 = 341
	line_length = 76*0.5 # Size of the lines in the shapes
	line_width = 14
	
	# We want the stimuli to have some body
	self.c.set_penwidth(1)
	
	# The angular separation of the stimuli depends on the number of items
	# CAREFUL: middle circle locations are actually manually defined below
	angular_separation_circle1 = 360.0 / 6
	angular_separation_circle2 = 360.0 / 12
	angular_separation_circle3 = 360.0 / 18
	
	# The first stimulis is drawn at angle 270 (12 clock position)
	angle = 270
	
	# Determine coordinates of center nontarget
	x = self.get("width") / 2
	y = self.get("height") / 2
	
	# Set the color of the stimulus
	self.c.set_fgcolor(nontarget_color)
	
	# draw the center nontarget
	draw_asp_stimulus(x, y, line_width, line_length, nontarget_orient, self.c)
	
	# Walk through all the stimuli - CIRCLE 1 - inner circle
	for color, orient in stimuli_circle1:
		
		# Determine the coordinates of the stimulus
		x = self.get("width") / 2 + radius_circle1 * math.cos(math.radians(angle))
		y = self.get("height") / 2 + radius_circle1 * math.sin(math.radians(angle))
	
		# Set the color of the stimulus
		self.c.set_fgcolor(color)
		
		# Draw special ASP stimulus which is an iterrupted bar looking like an i or bang
		draw_asp_stimulus(x, y, line_width, line_length, orient, self.c)
		
		# Make sure the next stimulus is drawn at a different location
		angle += angular_separation_circle1
	
	angle = 270
	
	# Walk through all the stimuli - CIRCLE 2 - inner circle
	for color, orient, location in stimuli_circle2:
	
		# Determine the coordinates of the stimulus
		x = self.get("width") / 2 + radius_circle2 * math.cos(math.radians(location))
		y = self.get("height") / 2 + radius_circle2 * math.sin(math.radians(location))
	
		# Set the color of the stimulus
		self.c.set_fgcolor(color)
	
		# Draw special ASP stimulus which is an iterrupted bar looking like an i or bang
		draw_asp_stimulus(x, y, line_width, line_length, orient, self.c)
		
		# Make sure the next stimulus is drawn at a different location
		angle += angular_separation_circle2
		
	angle = 270
	
	# Walk through all the stimuli - CIRCLE 3 - outer circle
	for color, orient in stimuli_circle3:
	
		# Determine the coordinates of the stimulus
		x = self.get("width") / 2 + radius_circle3 * math.cos(math.radians(angle))
		y = self.get("height") / 2 + radius_circle3 * math.sin(math.radians(angle))
	
		# Set the color of the stimulus
		self.c.set_fgcolor(color)
	
		# Draw special ASP stimulus which is an iterrupted bar looking like an i or bang
		draw_asp_stimulus(x, y, line_width, line_length, orient, self.c)
		
		# Make sure the next stimulus is drawn at a different location
		angle += angular_separation_circle3
	__end__

define inline_script trial_prep
	set description "Executes Python code"
	___run__
	#from random import randint
	
	#random_fix_interval = random.randint(700,1100)
	
	#self.experiment.set("fixation_interval", random_fix_interval)
	__end__
	set _prepare ""

define sequence trial_sequence
	set flush_keyboard yes
	set description "A single trial"
	run fixation_dot always
	run advanced_delay always
	run search_display always
	run keyboard_response always
	run error_display "[correct] = 0"
	run logger always

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.