xxxxxxxxxx
224
/*
notes:
since we're ranking similarity i really don't see the point of multiple sliders so other than (maybe) stylistic congruency and general edits (link to sheets etc.) i dont see what should change.
This similarity rating demo can load multiple images as stimuli and display 2 images at a time. The order is fully randomized. Image file names/numbers and rating values will be stored, and can be downloaded afterwards.
Instructions:
1. Upload images
2. Write image filenames
*/
// !! Change this to you own project's name in order to save your experiments data.
// The data can be found on the google drive
const SAVE_DATA_SHEET_NAME = "example_similarity";
const filenames = ['1.jpg', '2.jpg', '3.jpg', '4.jpg'];
const randomOrder = true;
// Here you can change the prompt the user gets.
const instruction = 'Please rate the similarity of the 2 objects showed above';
const leftLabel = 'not so similar';
const rightLabel = 'very similar';
var imgX1 = 100;
var imgX2 = 620;
var imgY = 30;
var imgSize = 480;
var lineY = imgY + imgSize + 160;
var images = [];
var sliderX1 = 150;
var sliderX2 = 1050;
var sliderBallRadius = 18;
var ratingValue;
var labelYOffset = 35;
var trialNumber = 1;
var imgMatrix = makeCombinations(filenames.length);
function preload() {
for (var i = 0; i < filenames.length; i++) {
images[i] = loadImage('img/' + filenames[i]);
}
}
function setup() {
if(randomOrder){
shuffleMatrix(imgMatrix);}
createCanvas(1200, 800);
data = new p5.Table();
data.addColumn('Image 1');
data.addColumn('Image 2');
data.addColumn('Similarity Ratings');
}
function draw() {
background(220);
if (trialNumber <= imgMatrix.length) {
image(images[imgMatrix[trialNumber - 1][0]], imgX1, imgY, imgSize, imgSize);
image(images[imgMatrix[trialNumber - 1][1]], imgX2, imgY, imgSize, imgSize);
fill(0, 102, 153);
noStroke();
textSize(22);
textAlign(LEFT);
text(instruction, 100, 560);
fill(80);
textSize(18);
text('Move mouse horizontally to adjust slider, click to respond and proceed to next trial.', 100, 590);
strokeWeight(4);
stroke(80);
line(sliderX1, lineY, sliderX2, lineY);
drawSliderTicks();
fill(158, 194, 208);
stroke(50);
strokeWeight(4);
ellipse(sliderX(mouseX), lineY, sliderBallRadius);
fill(0);
noStroke();
textSize(18);
text(leftLabel, sliderX1, lineY + labelYOffset);
textAlign(RIGHT);
text(rightLabel, sliderX2, lineY + labelYOffset);
} else {
//final page, data download instruction
fill(0, 102, 153);
noStroke();
textSize(28);
textAlign(CENTER);
text('The End', 600, 400);
fill(100);
textSize(20);
text('The data has been sent.', 600, 450);
}
}
function mouseClicked() {
if (trialNumber <= imgMatrix.length) {
ratingValue = round(100 * ratingX(mouseX));
// save the data
let newRow = data.addRow();
newRow.setString('Image 1', filenames[imgMatrix[trialNumber - 1][0]]);
newRow.setString('Image 2', filenames[imgMatrix[trialNumber - 1][1]]);
newRow.setNum('Similarity Ratings', ratingValue);
trialNumber++;
if(trialNumber > imgMatrix.length){
// Translate the p5 Table to a two dimensional array and send it to Google sheets
dataExport = data.getArray();
dataExport.unshift(data.columns);
saveToSheet(SAVE_DATA_SHEET_NAME, dataExport);
}
}
}
function sliderX(mx) {
var mq = max([mx, sliderX1]);
return min([mq, sliderX2]);
}
function ratingX(mx) {
var mq = max([mx, sliderX1]);
mq = min([mq, sliderX2]);
return map(mq, sliderX1, sliderX2, 0, 1);
}
function drawSliderTicks() {
var tickHeight = -8;
var nTicks = 6;
for (var i = 0; i < nTicks; i++) {
var xtemp = map(i, 0, nTicks - 1, sliderX1, sliderX2);
line(xtemp, lineY, xtemp, lineY - tickHeight);
}
}
function makeCombinations(n) {
//create a 2 by n matrix
holder = [];
for (var i = 0; i < n; i++) {
for (var j = i; j < n; j++) {
holder.push([i, j]);
}
}
return holder;
}
function shuffleMatrix(matrix){
shuffle(matrix, true);
for (var i = 0;i<matrix.length;i++) {
shuffle(matrix[i], true);
}}
/**
* This function saves the data to a google sheet of the given name.
* Make sure that the name is unique for you project!
* If your name is not unique then you will share a google sheet with someone else.
*
*
* @param {string} sheetName - The name of you sheet. Make sure it is unique to your project.
* @param {Array<Array>} data - A two dimensional array representing the data.
* Data example:
* data = [
* (row1:) [col1, col2, col3],
* (row2:) [col1, col2, col3]
* ]
* @param {string} [tabName] - (OPTIONAL) The name of the tab/worksheet that this instance of data will be in.
* If you leave this empty it will fall back to using the current time of data submission as tab name
* IMPORTANT! if you are using custom tab names make sure that every name is unique.
* If a name already exists saving wil result in an error.
*/
async function saveToSheet(sheetName, data, tabName) {
// Safety check
if (!sheetName) {
return console.error("The name for the sheet was not valid!")
}
if (!data || data.length === 0) {
return console.error("Cannot save empty data!")
}
// Building save request
const requestBody = {
sheetName,
data
}
// Add tabName if it exists
if (tabName && typeof tabName === "string") {
requestBody.tabName = tabName
}
try {
// Send the save request
const response = await fetch("https://node.bykrijgsman.com/save-data", {
method: "POST",
mode: "cors",
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestBody)
})
const result = await response.text()
console.log(result)
} catch (e) {
// There was an error while saving if we get here!
console.error(e)
}
}