PennController for IBEX › Forums › Support › Creating visual stimuli from a csv
- This topic has 7 replies, 3 voices, and was last updated 2 years, 4 months ago by
Jeremy.
-
AuthorPosts
-
April 4, 2022 at 10:28 am #8006
inthatb
ParticipantHi 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 2 years, 5 months ago by
inthatb.
April 4, 2022 at 2:45 pm #8010Jeremy
KeymasterHi 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 whenr5c5
is 0 you also print a white square at the exact same position, resulting in a visually blank gridIndependently 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
April 5, 2022 at 1:10 pm #8014inthatb
ParticipantHi 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
April 7, 2022 at 9:00 am #8018inthatb
ParticipantI 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 themDo you have thoughts?
My new link: https://upenn.pcibex.net/r/NfPXvD/
April 7, 2022 at 9:58 am #8019florians
ModeratorYou 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())April 7, 2022 at 8:59 pm #8021inthatb
ParticipantHm… 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.April 7, 2022 at 9:20 pm #8022florians
ModeratorAh, 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.
April 11, 2022 at 11:52 am #8031Jeremy
KeymasterHi,
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 usinghidden
andvisible
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 contentJeremy
-
This topic was modified 2 years, 5 months ago by
-
AuthorPosts
- You must be logged in to reply to this topic.