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.

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

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.

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.

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 !.

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

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.

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.

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.

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.

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.

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.

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.

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.

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.

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

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.

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

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.

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.

You may also like...

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.