Creating visual stimuli from a csv

PennController for IBEX Forums Support Creating visual stimuli from a csv

Viewing 8 posts - 1 through 8 (of 8 total)
  • Author
    Posts
  • #8006
    inthatb
    Participant

    Hi Jeremy,
    I’m working on a project for LING456 with Prof. Schwarz.
    In this project, I need to create a 5×5 grid. Some of these grid positions will be populated with dots (encoded in a 25 column csv).

    I’ve implemented this as test conditions with variables reading in a csv encoding the dot positions. However, the grid isn’t showing up. There is no error message but the screen is blank. Do you know what could be happening?
    Related – is there a way to use loops to remove duplicate code?

    Here’s the link: https://farm.pcibex.net/r/PhfcsA/

    Thanks,
    Inthat

    • This topic was modified 1 year, 11 months ago by inthatb.
    #8010
    Jeremy
    Keymaster

    Hi Inthat,

    You might be interested in this guide on using Javascript in your code: you don’t need to create Var elements to execute different code depending on the value of the cells from the current row

    Other than that, most of your trials do show a black square, but in some cases a white square is printed over the black square. For example, when r5c1 is 1 you print a black square at the bottom-left corner of the grid, and when r5c5 is 0 you also print a white square at the exact same position, resulting in a visually blank grid

    Independently of that, I suspect that you mean to create 25 Image elements (one per cell on your grid) but you currently create two Image elements that you move around with each test. My suggestion is, use Javascript ternary conditionals to create an Image element with the appropriate file for each cell:

    Template("dotmemorization.csv", row =>
        newTrial("experimental-trial-pic",
            newSelector("input")
            ,
            defaultImage.size(40,40).css("border", "solid 1px gray").selector("input")
            ,
            newCanvas("grid", 200,200)
              // row 1
              .add(  0,   0, row.r1c1==1?newImage("black.png"):newImage("white.png") )
              .add( 40,   0, row.r1c2==1?newImage("black.png"):newImage("white.png") )
              .add( 80,   0, row.r1c3==1?newImage("black.png"):newImage("white.png") )
              .add(120,   0, row.r1c4==1?newImage("black.png"):newImage("white.png") )
              .add(160,   0, row.r1c5==1?newImage("black.png"):newImage("white.png") )
              // row 2
              .add(  0,  40, row.r2c1==1?newImage("black.png"):newImage("white.png") )
              .add( 40,  40, row.r2c2==1?newImage("black.png"):newImage("white.png") )
              .add( 80,  40, row.r2c3==1?newImage("black.png"):newImage("white.png") )
              .add(120,  40, row.r2c4==1?newImage("black.png"):newImage("white.png") )
              .add(160,  40, row.r2c5==1?newImage("black.png"):newImage("white.png") )
              // row 3
              .add(  0,  80, row.r3c1==1?newImage("black.png"):newImage("white.png") )
              .add( 40,  80, row.r3c2==1?newImage("black.png"):newImage("white.png") )
              .add( 80,  80, row.r3c3==1?newImage("black.png"):newImage("white.png") )
              .add(120,  80, row.r3c4==1?newImage("black.png"):newImage("white.png") )
              .add(160,  80, row.r3c5==1?newImage("black.png"):newImage("white.png") )
              // row 4
              .add(  0, 120, row.r4c1==1?newImage("black.png"):newImage("white.png") )
              .add( 40, 120, row.r4c2==1?newImage("black.png"):newImage("white.png") )
              .add( 80, 120, row.r4c3==1?newImage("black.png"):newImage("white.png") )
              .add(120, 120, row.r4c4==1?newImage("black.png"):newImage("white.png") )
              .add(160, 120, row.r4c5==1?newImage("black.png"):newImage("white.png") )
              // row 5
              .add(  0, 160, row.r5c1==1?newImage("black.png"):newImage("white.png") )
              .add( 40, 160, row.r5c2==1?newImage("black.png"):newImage("white.png") )
              .add( 80, 160, row.r5c3==1?newImage("black.png"):newImage("white.png") )
              .add(120, 160, row.r5c4==1?newImage("black.png"):newImage("white.png") )
              .add(160, 160, row.r5c5==1?newImage("black.png"):newImage("white.png") )
              .print()
            ,
            newButton("press")
                .print()
                .wait()
        )
    )

    Jeremy

    #8014
    inthatb
    Participant

    Hi Jeremy,
    That’s really helpful, thank you!
    I’m trying to set up the grid so that participants will see a grid. After a brief pause, they will see another grid (either the same or different by 1 dot) and asked whether this grid matches the previous grid. Prof. Schwarz suggested that I don’t encode position in the csv but rather use shuffle() on the selector. In the first two rows of the csv, I would have one dot and one blank respectively. Then, I can flip the value of these bits to get a new grid that differs by one.
    I’ve tried implementing it here but have run into three issues. First, I need to shuffle the canvas rather than the selector. However, there doesn’t seem to be a shuffle function for canvas. Second, I don’t know how we can change the image associated with a handle. I need to do this to flip a square to a dot. Third, line 174 doesn’t seem to be showing the grid again after the timer named “wait2” has run out. Do you know how I could fix this?

    Inthat

    #8018
    inthatb
    Participant

    I have made progress – I’ve initialized two squares as dots. To generate a match/mismatch case (indicated by the csv), I’ve reserved squares 1 and 2 as dots. I initially show square 1 and hide square 2. When we get to the mismatch case, I hide square 1 and show square 2 to flip the positions of 2 dots while making the total number of dots the same. There is an issue here – the hidden stimuli don’t have the 1 pixel gray border and make the grid look strange.
    I’m trying to do a similar thing with words. I’m going to show a word list then present them with a match/mismatch condition. I think that the best way to do this is to read in a csv row with #elements + 1, then hide/unhide two words to switch them

    Do you have thoughts?

    My new link: https://upenn.pcibex.net/r/NfPXvD/

    #8019
    florians
    Moderator

    You need to always have an image in each cell, whether it’s white or black, to keep the grid even. So introduce either white or black at first, and then hide previous and make new image visible in same location (black -> white or white -> black).

    newImage(“sq1”, “black.png”)
    ,
    newImage(“sq1b”, “white.png”)
    ,
    newImage(“sq2a”, “white.png”)
    ,
    newImage(“sq2”, “black.png”)
    .hidden()
    ,
    newCanvas(“grid”, 200,200)
    // row 1
    .add( 0, 0, row.r1c1==1?getImage(“sq1”):getImage(“sq1”) )
    .add( 40, 0, getImage(“sq2a”))//row.r1c2==1?getImage(“sq2a”):getImage(“sq2a”) )
    .add( 40, 0, getImage(“sq2”))//row.r1c2==1?getImage(“sq2”):getImage(“sq2”) )

    […]

    getVar(“match”).test.is(“False”)
    .success(getImage(“sq1”).hidden(), getImage(“sq1b”).visible(),getImage(“sq2a”).hidden(), getImage(“sq2”).visible())

    #8021
    inthatb
    Participant

    Hm… this solution doesn’t work for me? I think the issue is with shuffle – when I don’t shuffle the canvas, the dot switching works fine. However, when I have shuffle, there are sometimes 0,1,2 gaps. I think that this is because shuffle actually chooses 25/27 possibilities and sometimes those choices contain the hidden squares (so we have gaps).
    Also, can you take a look at my jump conditional? The no-interference trial works fine but I think I’m having trouble triggering the jump with an incorrect response.

    #8022
    florians
    Moderator

    Ah, of course – sorry, didn’t try it out, but you’re right, those new elements are now of course shuffled separately. I think the solution is to actually create the shapes directly in PCIbex, rather than using a white.png and black.png image; and then just superimpose them on a pre-generated grid. This way, you can easily hide and make visible the same image (or else update its color properties). e.g.

    newCanvas(“black”, 50, 50).color(“black”).css(“border-radius”,”100%”)

    Will give you a black dot, which you can update to white or hide it to make it disappear etc.

    #8031
    Jeremy
    Keymaster

    Hi,

    At this point you’d be better off handling the shuffling part using javascript:

    Template("dotmemorization.csv", row =>
        newTrial("dot-baseline",
            defaultImage.size(40,40).css("border", "solid 1px gray")
            ,
            newCanvas("grid", 200,200)
            ,
            rs = [...new Array(25)].map((v,i)=>(i<row.nDots?true:false))
            ,
            fisherYates(rs)
            ,
            rs.map((v,i)=>[
                newImage('w'+i,'white.png').print(40*(i%5),40*Math.floor(i/5),getCanvas("grid"))[v?'hidden':'visible'](),
                newImage('b'+i,'black.png').print(40*(i%5),40*Math.floor(i/5),getCanvas("grid"))[v?'visible':'hidden']()
            ])
            ,
            getCanvas("grid", 200,200).center().print()
            ,
            // display for X seconds
            newTimer("display-for-3", 3000)
                .start()
                .wait()
            ,
            newCanvas("overGrid", 200,200).color("white").print(1,1,getCanvas("grid"))
            ,
            newTimer("blank-for-3", 3000)
                .start()
                .wait()
            ,
            (row.match=="False"?[
                getImage('b'+rs.findIndex(v=>v===true)).hidden(),
                getImage('w'+rs.findIndex(v=>v===true)).visible(),
                getImage('b'+rs.findIndex(v=>v===false)).visible(),
                getImage('w'+rs.findIndex(v=>v===false)).hidden()
            ]:null)
            ,
            getCanvas("overGrid").remove()
            ,
            newText("prompt", "This grid matches the previous grid.")
            ,
            newButton("True","True")
            ,
            newButton("False","False")
            ,
            newCanvas("choice", 200,200)
                .add(0, 0, getText("prompt"))
                .add(0, 50, getButton("True"))
                .add(50, 50, getButton("False"))
                .center()
                .print()
            ,
            newVar("answer")
            ,
            newSelector("choice")
                .add(getText("prompt"))
                .add(getButton("True"))
                .add(getButton("False"))
                .wait()
                .setVar("answer")
                .log()
                .test.selected( getButton(row.match) )
                .failure( jump("no-interference") )
        )
    )

    Note that you need to add a column named nDots to your table, reporting how many cells on the grid should use black.png. I got rid of the Selector element, since you’re not making use of it (you’re not asking the participant to click on a cell). There also seems to be a bug with using hidden and visible on the Canvas element after using those same commands on the Image elements that it contains, so I resorted to printing a second, opaquely white Canvas element on top of it to mask its content

    Jeremy

Viewing 8 posts - 1 through 8 (of 8 total)
  • You must be logged in to reply to this topic.