Elementarraystim - applying a texture/mask to the field rather than the elements?

Hi everyone!

I am currently programming a so-called “dot lattice” stimulus that consist of a grid of regularly spaced dots. As the ElementArrayStim is suitable to easily display such stimuli, I started to use this object. However, in the future I would probably want to add a Gaussian window to this stimulus. This window should be applied to the whole stimulus rather than each object separately, as shown in the image below.

Am I right that it currently is not possible to apply such a “field-wide” Gaussian mask to an ElementArrayStim? What would be the best strategy to create such a stimulus? First, create an image of the stimulus, load it again, and multiply this texture with a Gaussian? I don’t feel literally stuck here, but I am curious whether there are elegant solutions to this problem.

You could perhaps do something like the following to get a gaussian mask (assuming dots is an ElementArrayStim):

dots.opacities = np.prod(
    [
        psychopy.filters.makeGauss(xys, sd=np.ptp(xys) / 6.0)
        for xys in dots.xys.T
    ],
    axis=0
)

This would only really work properly if the elements are not overlapping, and you would probably want to set sd to be specific to the size of the field.

Thanks for this suggestion! At first, I was thinking that it might be problematic that a single dot might receive the same opacity value everywhere, but from the stimulus examples I have available, this seems to be the case also. I will mark this as the final solution.

Yes, fair point about being constant within an element. The below is somewhat inelegant but would seem to work more completely:

import numpy as np

import psychopy.visual
import psychopy.event

import pyglet.gl

bg_col = -1.0

win = psychopy.visual.Window(
    size=[400, 400],
    units="pix",
    color=[bg_col] * 3,
    fullscr=False
)

n_dots = 500

dots = psychopy.visual.ElementArrayStim(
    win=win,
    nElements=n_dots,
    xys=np.random.uniform(-120, 120, (n_dots, 2)),
    units="pix",
    sizes=5.0,
    elementTex=None,
    elementMask="circle"
)

mask_tex = psychopy.visual.GratingStim(
    win=win,
    tex=np.ones([256, 256]) * bg_col,
    mask="gauss",
    units="pix",
    size=[256, 256]
)

dots.draw()

pyglet.gl.glBlendFunc(
    pyglet.gl.GL_ONE_MINUS_SRC_ALPHA,
    pyglet.gl.GL_SRC_ALPHA
)

mask_tex.draw()

pyglet.gl.glBlendFunc(
    pyglet.gl.GL_SRC_ALPHA,
    pyglet.gl.GL_ONE_MINUS_SRC_ALPHA
)

win.flip()

psychopy.event.waitKeys()

win.close()

Thanks again for this great suggestion! It seems I can only mark a single answer as the final solution, so I will leave the first one as it seems the best trade-off between what people will want to achieve and simplicity of the code.