kurokida / jspsych-psychophysics

A jsPsych plugin for psychophysics
https://kurokida.github.io/jspsych-psychophysics/
MIT License
53 stars 14 forks source link

Change image interactively #30

Closed Qiuishere closed 2 years ago

Qiuishere commented 2 years ago

Hi Kurokida! I'm trying to achieve that when you press uparrow or downarrow, the image presented will be changed. For this I'm using the same logic as in keyboard_event.html demo, only replacing the circle_obj with my image object. This part of script looks like this.

    var theImage = {
        obj_type: 'image',
        file: 'stimuli/Front/Fe_Front_45L.png',
        horiz_pix_sec: 0,
        show_start_time: 0,
        startX: 'center',
        startY: 'center',
    }

    const adjust= {
        type: 'psychophysics',
        canvas_height: 500,
        prompt: '<p>Pressing the ArrowUp/ArrowDown key to adjust<br>Press the space key to finish the program.</p>',
        stimuli: [theImage], // These can be referenced using the jsPsych.currentTrial().stimuli array.
        response_type: 'key',
        choices: [' '],
        key_down_func: function(event){ 
            if (event.key === 'ArrowUp'){
                theang = 54;
            } else if (event.key === 'ArrowDown'){
                theang = 36;
            }

            jsPsych.currentTrial().stim_array[0].file = "stimuli/Front/Fe" + "_" + "Front" + "_" + theang + "L" + ".png"
        },
    }

However, this doesn't change the image presented on the screen, although this works well for other properties of other object, e.g. angle of a line object. Also I confirmed that, the .file property of the theImage object did change after I press the key, i.e. if I press Uparrow and then run jsPsych.currentTrial().stim_array[0].file, I will get 'stimuli/Front/Fe_Front_54L.png', rather than the previous 'stimuli/Front/Fe_Front_45L.png'. It's just the .file property didn't update the image presented on the screen.

Do you have idea why this happens? Please let me know if you need more information! Thanks so much! Qiu

kurokida commented 2 years ago

Thank you for using my plugin.

Sorry, but the properties that can be changed in the key_down_func are limited. Images cannot be changed.

You should use looping timelines. This page might be useful.

Qiuishere commented 2 years ago

Hi Kurokida,

Thanks so much! Your solution works pretty well. Thanks for the great help and also for your very useful plugin. Good day!

Qiuishere commented 2 years ago

Hi Kurokida,

May I ask a follow-up question? I used the loop_function, it can achieve adjusting pictures by keypress, but only once at each keypress. So every time you need to press and release and then press again. Continuously pressing would be considered as just one press. Considering in our experiment the subjects might need to change pictures for dozens of times to get the right one, this is indeed time-consuming. I'm wondering is it possible to continuously adjust the image file property? Just as you did in key_down_func, but for the "file" property ofobj_type: 'image'. Will this function come out soon? If yes then I'm really looking forward to it. Or is it possible for me to achieve that on the basis of psychophysics plug-in myself? If it's achievable I'd be happy to do so.

Thanks!

kurokida commented 2 years ago

I am sorry, but I still do not fully understand what you are having trouble about. Do you want to continuously change the image without pressing any keys? Could you show me the code?

Qiuishere commented 2 years ago

Hi Kurokida,

Thanks for your quick reply and sorry for my late reply! I did almost the same as you recommended, i.e. loop_function with key press to change the picture presented.

    const adjustment = {
        type: 'image-keyboard-response',
        stimulus: function(data){
            if (jsPsych.data.get().last(1).values()[0].stimulus == 'stimuli/masks/' + maskimg[Nmask-1] + '.jpg'){
                initImg = jsPsych.data.get().last(5).values()[0].stimulus;
                initAng = parseInt(initImg.match(/\d+/g));
                theang = initAng;
                currentImg = initImg;
            } else {

                currentImg = initImg.replace(initAng,theang)
            }
            return currentImg;
        },
        stimulus_width: imgSize,
        response_type: 'key',
        choices: [' ', 'ArrowUp', 'ArrowDown'],
    }

    const loop_node = {
        timeline: [adjustment],
        data: jsPsych.timelineVariable('data'),
        loop_function: function(data){
            if (data.values()[0].response === 'arrowup'){
                theang -= 3;
                return true

            } else if (data.values()[0].response === 'arrowdown'){ // arrowdown
                theang += 3;
                return true

            } else if (data.values()[0].response === ' '){
                return false
            }
        }
    }
    aTrial.push(loop_node)

So every time I press uparrow or downarrow, the image will change. However, with this, every time I press the key the picture changes only ones. I have to press-release-press again to change multiple times. So if I want to change to the 20th image from the 1st image, I have to press 19 times. What I'd like to achieve is that the image can be changed continuously if I press continuously, like what you did in key_down_func. I know with key_down_func I can change properties continously (without having to release the key and press again) like angle, position, etc. of a line or circle object. I'd like to know if it's possible to adapt the plug-in to change the file property of an image object continously? I believe it will be useful for lots of experiments using images. Could you give me some help?

Thanks!

kurokida commented 2 years ago

Thank you for your explanation. Now I uderstand your problem. I added the sample program. I hope you like it. Note that you should use the latest version of my plugin.

The method used in this demonstration is one that I rarely explain on my site. Sorry for the lack of information.

Qiuishere commented 2 years ago

Thanks! This is exactly what I need. Thanks so much for adding this sample for me. It's very clear and easy to use. (In case it's useful for others: I still use the old version of jspsych-psychophysics-2.3.2, and changedjsPsych.GetCurrentTrial in the sample tojsPsych.currentTrial(), it also works well) Thanks! Good night!

kurokida commented 2 years ago

Thank you for your reply. If you use an older version of my plugin, be aware that it contains some bugs. See the releases page.

Especially in experiments using high-resolution displays such as Mac, stimuli may appear in inappropriate positions. If the stimuli are only images, I think the problem wouldn't occur.

Qiuishere commented 2 years ago

Thanks for your reminder! I indeed met with problems about image size. I tried to change the size of the images using

const temporal_object = {
                    obj_type: 'image',
                    file: initImg.replace(initAng,theang),
                    image_width: 2,
                    drawFunc: function(stimulus, canvas, context){} // This is specified to prevent the image from being presented.
                }

here the image_widthdoes not change anything. I also tried width. May I ask what's the right way to control image size in this case?

The reason that I went back to the 2.3.2 was that when I use the newest plugin by putting <script src="jspsych-psychophysics-master/jspsych-psychophysics.js"></script> at the beginning and type: 'jsPsychPsychophysics', or type: psychophysics', inside the trial, it always reports error : No plugin loaded for trials of type "jsPsychPsychophysics jspsych.js:906 " . I'm using Jspsych 6.3, so I thought maybe Jspsych 6.3 is not compatible with Jspsych-psychophysics 3. But if there is a way to use the new version of Jspsych-psychophysics on 6.3, I will be very happy to use that.

I feel I really used a lot of your time. Thank you for your patience!

Qiuishere commented 2 years ago

Please pardon my further question: is there any way to write data within key_down_func? Now data can be written in on_start and on_finish of this trial, but since our experiment needs to record the time and the key for the first keypress, we tried to put data.rt = under key_down_func. Bur got the error

Uncaught TypeError: Cannot set properties of undefined (setting 'rt')
    at HTMLDocument.key_down_func 

So it seems it's not allowed to write data here? Is there any way to record the response of the first keypress? Thank you so much !!!

kurokida commented 2 years ago

Sorry but, you can't specify the image_width property as long as you use the old version of my plugin. And as you said, you should use the jsPsych V7 to be comatible with my latest plugin.

To store the data which can be obtaind in the key_down func, you should assign the data to a global variable. I will show you the code how to save a brightness data when you run the keyboard_event demo.

let brightness; // This is a global variable.

const trial = {
    type: jsPsychPsychophysics,
    pixi: pixi_flag,
    canvas_height: 500,
    prompt: '<p>Pressing the ArrowUp/ArrowDown key, the color of the circle will change. <br>Press the space key to finish the program.</p>',
    stimuli: [circle_obj], // These can be referenced using the jsPsych.currentTrial().stimuli array.
    response_type: 'key',
    choices: [' '],
    key_down_func: function(event){ // The key_up_func is also available. In that case, the color of the circle changes when you release the key. 
      if (event.key === 'ArrowUp'){
        current_color += 10;
        if (current_color > 255) current_color = 255;
      } else if (event.key === 'ArrowDown'){
        current_color -= 10;
        if (current_color < 0) current_color = 0;
      }

      brightness = current_color; // assign to the global variable. This variable is overwritten each time a key is pressed.

      // Note that when specify the space bar, you need to write event.key === ' ' not event.key === 'space'.

      jsPsych.getCurrentTrial().stim_array[0].fill_color = `rgb(${current_color}, ${current_color}, ${current_color})`;
    },
    on_finish: function(data){
        data.brightness = brightness;
    }
}
Qiuishere commented 2 years ago

Hi Kurokida,

Thanks for your explanation! You have lent so much help to our project. We feel it is only fair to acknowledge you in our paper besides citing your plugin.

Good day!