lineWidth for circle stimulus

Is there a maximum value for lineWidth? I can see a difference between 1 and 20 but cannot see an increase in width once over 20. If I want the line width to be bigger, is there anyway to do that (equivalent of strokewidth in javascript).

The unit for my window is set of “pix” but I think the default setting of linewidth unit is pix.

Many thanks in advance!

Sophie

1 Like

I’m not sure if 20 pixels is the maximum line width but you could create a donut with two filled circles with the one in front being the same colour as the background.

The donut method works but involves changing the code quite a bit! Is there a way to make the linewidth bigger than 20? I am also using a mac so this could be why I am not seeing any changes going above 20? Thanks!

Linewidth does max out at 20 I believe, because of the underlying Python library used to draw stimuli. One way to get a donut effect with a transparent middle and minimal faff is to create a circle and then in the Begin Routine tab of a :code: Code component do:

myCircle.vertices = [myCircle.vertices, myCircle.vertices * (1 - propLineWidth)]

where propLineWidth is the desired line width as a percentage of the circle’s radius - e.g. for an outline of 40px on a circle that’s 500px by 500px, it would be 40/250.

3 Likes

Thanks! I just tried to implement the code but it doesnt work. Am I setting the vertices wrong?

r = 150
circle = visual.Circle(myWin, radius=r, edges=13, units="pix", interpolate = True,opacity = 1, fillColor="blue", lineColor="blue", autoDraw= True, pos=(0,0))
propLineWidth = 40/r
circle.vertices = [circle.vertices, circle.vertices * (1 - propLineWidth)]


The blue line was generated by the circle code.

Thanks!

This is a bit of an obscure rule, and one that I’d like to eventually make obsolete, but because CircleStim is a child of BaseShapeStim rather than ShapeStim it doesn’t inherit the methods necessary for tessellation, so that’s why the donut thing doesn’t work from the (seemingly perfectly reasonable!) code you’ve written. From Builder it does work as the :polygon: Polygon component will make a ShapeStim when its shape is set to “circle”. You can alter your code to make it work like so:

win = visual.Window()
r = 150
circle = visual.ShapeStim(
    win, vertices="circle",
    pos=(0, 0), size=(r*2, r*2), units="pix",
    fillColor="blue", lineColor="blue", opacity=1, interpolate=True,
    autoDraw=True)
propLineWidth = 40/r
circle.vertices = [circle.vertices, circle.vertices * (1 - propLineWidth)]

In short, use a visual.ShapeStim with vertices set to “circle” rather than using a visual.Circle.

1 Like

Hi! It worked!!! Thanks a bunch! I am trying to replicate the same logic on an arc that I am drawing, generated by ShapeStim.

bucket = visual.ShapeStim(myWin, pos = (0,0), lineColor = "gold", autoDraw= True, lineWidth = 50)
vertices_arc = np.array([[r*math.cos(bucket_range), r*math.sin(bucket_range)] for bucket_range in np.arange(0, 30, 0.01)])
bucket.setVertices(vertices_arc, vertices_arc* (1 - propLineWidth))
bucket.closeShape = False 

But I ran into the same issue as before when making a circle with visual.circle, specifically the arc becomes very small (the arc is supposed to be 30 degrees)

Is it possible to replicate the logic in a self-defined set of vertices?

Thanks!

1 Like

vertices_arc is a list, meaning it doesn’t know how to be multiplied by a number as a numpy.Array does. You need to do:

vertices_arc = np.array(vertices_arc)

Thanks - I converted to np.array! This is what I have but the arc doesnt show on myWin

def produce_vertices (bucket_size, location_x, location_y):
    location_radians = math.atan2(location_y, location_x)
    bucket_range = [location_radians - (np.deg2rad(bucket_size)/2), location_radians + (np.deg2rad(bucket_size)/2)]
    temporary_arc = [[r*math.cos(bucket_range)+center[0], r*math.sin(bucket_range)+center[1]] for bucket_range in np.arange(bucket_range[0], bucket_range[1] + 0.01, 0.01)]
    temporary_arc = np.array(temporary_arc)
    return temporary_arc
vertices_arc = produce_vertices(bucket_size = 30, location_x = 150, location_y = 90)

# Draw bucket
bucket = visual.ShapeStim(myWin, pos = (0,0), lineColor = "gold", size=(r*2, r*2), autoDraw= True, units="pix")
bucket.vertices = [vertices_arc, vertices_arc * (1 + propLineWidth)]

produce_vertices is a function that returns an array of x, y coordinates for the vertices of the arc based on the degree and location of the mouse.
I got

Am I scaling the vertices in the wrong way?

What output are you getting from the function to make the arc vertices? Do they seem to make sense compared to the vertices of the blue circle?

Also as you already have the vertices of the circle, you could just construct the arc from these. Rather than setting the vertices of the ShapeStim to be "circle", set it to be 360. This means it’ll be an equilateral with 360 points, which is essentially a circle. Then store the initial vertices in a variable (let’s call it ogCircleVerts) and use that to make the arc like so:

def produce_vertices(bucket_size):
    verts = ogCircleVerts[:int(bucket_size)]
    return np.array([verts, verts * (1 - propLineWidth)])

They are on different magnitude. The circle coordinates are mostly in the magnitude of e-01), while the arc ones are mostly in the 10th. Here I am pasting the first 5 for [circle.vertices, circle.vertices * (1-propLineWidth)]
[[[ 0.00000000e+00 5.00000000e-01]
[ 3.13952598e-02 4.99013364e-01]
[ 6.26666168e-02 4.96057351e-01]
[ 9.36906573e-02 4.91143625e-01]
[ 1.24344944e-01 4.84291581e-01]
… …
]
[[ 0.00000000e+00 3.66666667e-01]
[ 2.30231905e-02 3.65943134e-01]
[ 4.59555190e-02 3.63775390e-01]
[ 6.87064820e-02 3.60171992e-01]
[ 9.11862920e-02 3.55147159e-01]
… …]]

Here are vertices of the arc
[[[144.21537944 41.25438562]
[143.79563175 42.69445268]
[143.36150462 44.13025032]
[142.91304145 45.56163498]
… …
]

[[182.67281396 52.25555512]
[182.14113355 54.07964006]
[181.59123918 55.89831708]
[181.02318584 57.71140431]
[180.43703033 59.51872045]
… …]]

Is your circle using the same units as your arc?

Thanks! But the vertices of the arc won’t be determined by the location of the mouse. Location of the mouse matters to the experiment because the bucket (aka arc) will be dragged by the mouse along the circle. Unless there is a way to slice the circle vertices based on mousex and mousey?

Yes! The circle uses the same units as the arc (in particular, r = 150, units = “pix”, size = (1502, 1502))

I’m guessing based on a quick read, but is bucket_range in your produce_vertices function effectively a start and stop index? What kind of values do you get if you print bucket_range?

Here is an example: [0.27862011247143476, 0.8022188880697336] in radians
Bucket range is determined by the bucket size (in radians) with where the mouse are (i.e., location_radians = using math.atan2 to convert x,y coordinates into angle on the circle)
bucket_range = [location_radians - (np.deg2rad(bucket_size)/2), location_radians + (np.deg2rad(bucket_size)/2)]

For some reason, the arc depends on closeShape = True/False

I adapted your code to adapt for mouse location and while experimenting with code I realize setting closeShape to different values give different results.

When closeShape = True, it looks like:


When closeShape = False, it looks like:

Here is the complete code


#initialise screen and mouse
display = (1000, 700)

# creates a window
myWin = visual.Window(color='grey', units='pix', size=display, monitor='testMonitor',
                      allowGUI=False, fullscr=False, pos = (0, 0))
mouse = Mouse(visible=True)

# parameters to draw on gui
center = (0, 0)  # position of the circle
r = 150  # radius, in whatever units the window uses

# helper function
model_circle = visual.ShapeStim(myWin, vertices=100, pos=(0, 0), size=(r*2, r*2), units="pix", fillColor="blue", lineColor="blue", opacity=1, interpolate=True, autoDraw=False)
propLineWidth = 20/r
ogCircleVerts = model_circle.vertices

def produce_vertices(ogCircleVerts, bucket_size, location_x, location_y):
    location_radians = math.atan2(location_y, location_x)
    location_degrees = np.rad2deg(location_radians)
    bucket_range = [location_degrees - bucket_size/2, location_degrees + bucket_size/2]
    verts = ogCircleVerts[int(bucket_range[0]):int(bucket_range[1])]
    print (verts)
    return np.array([verts, verts * (1 - 40/r)])
    
circle = visual.ShapeStim(myWin, vertices="circle", pos=(0, 0), size=(r*2, r*2), units="pix", fillColor="blue", lineColor="blue", opacity=1, interpolate=True, autoDraw=True)
circle.vertices = [circle.vertices, circle.vertices * (1 - propLineWidth)]


# Draw bucket
vertices_arc = produce_vertices(ogCircleVerts, 30, 150, 90)
bucket = visual.ShapeStim(myWin, vertices=vertices_arc, pos=(0, 0), size=(r*2, r*2), units="pix", fillColor="gold", lineColor="gold", opacity=1, interpolate=True, autoDraw=True)
bucket.closeShape = False

I am not overly familar with the source code so I dont quite know why it is happening…