1
0
mirror of https://github.com/psychopy/psychojs.git synced 2025-05-10 10:40:54 +00:00
psychojs/src/test_experiment.js
2024-03-29 01:38:10 +00:00

511 lines
15 KiB
JavaScript

/**************
* Gabor Test *
**************/
// import { core, data, sound, util, visual } from '../../psychojs_experimental/psychojsPR519.js';
// import { core, data, sound, util, visual } from "../out/psychojs-2024.1.0.js";
import { core, data, sound, util, visual } from "./index.js";
// import {StimInspector} from 'https://run.pavlovia.org/lgtst/stiminspector/StimInspector.js';
// import {StimInspector} from '../stiminspector/StimInspector.js';
// import {PsyexpReader} from '../psyexpreader/PsyexpReader.js';
const { PsychoJS } = core;
const { TrialHandler } = data;
const { Scheduler } = util;
//some handy aliases as in the psychopy scripts;
const { abs, sin, cos, PI: pi, sqrt } = Math;
const { round } = util;
// store info about the experiment session:
let expName = 'gabor'; // from the Builder filename that created this script
let expInfo = {};
// Start code blocks for 'Before Experiment'
// init psychoJS:
const psychoJS = new PsychoJS({
debug: true
});
window.psychoJS = psychoJS;
window.util = util;
// open window:
psychoJS.openWindow({
fullscr: false,
color: new util.Color("gray"),
units: 'height',
waitBlanking: true
});
// new StimInspector(psychoJS.window, { core, data, sound, util, visual });
// schedule the experiment:
psychoJS.schedule(psychoJS.gui.DlgFromDict({
dictionary: expInfo,
title: expName
}));
const flowScheduler = new Scheduler(psychoJS);
const dialogCancelScheduler = new Scheduler(psychoJS);
psychoJS.scheduleCondition(function() { return (psychoJS.gui.dialogComponent.button === 'OK'); }, flowScheduler, dialogCancelScheduler);
// flowScheduler gets run if the participants presses OK
flowScheduler.add(updateInfo); // add timeStamp
flowScheduler.add(experimentInit);
// flowScheduler.add(instructRoutineBegin());
// flowScheduler.add(instructRoutineEachFrame());
// flowScheduler.add(instructRoutineEnd());
flowScheduler.add(gaborRoutineBegin());
flowScheduler.add(gaborRoutineEachFrame());
flowScheduler.add(gaborRoutineEnd());
flowScheduler.add(quitPsychoJS, '', true);
// quit if user presses Cancel in dialog box:
dialogCancelScheduler.add(quitPsychoJS, '', false);
psychoJS.start({
expName: expName,
expInfo: expInfo,
configURL: "../config.json",
resources: [
{
name: "starformation.jpg",
path: "./test_resources/starformation.jpg"
},
{
name: "cool.gif",
path: "./test_resources/cool.gif"
},
{
name: "delorean.gif",
path: "./test_resources/delorean.gif"
},
{
name: "silverhand.gif",
path: "./test_resources/silverhand.gif"
},
// {
// name: "007",
// path: "007.jpg"
// },
]
});
psychoJS.experimentLogger.setLevel(core.Logger.ServerLevel.WARNING);
var frameDur;
async function updateInfo() {
expInfo['date'] = util.MonotonicClock.getDateStr(); // add a simple timestamp
expInfo['expName'] = expName;
expInfo['psychopyVersion'] = '2021.3.0';
expInfo['OS'] = window.navigator.platform;
// store frame rate of monitor if we can measure it successfully
expInfo['frameRate'] = psychoJS.window.getActualFrameRate();
if (typeof expInfo['frameRate'] !== 'undefined')
frameDur = 1.0 / Math.round(expInfo['frameRate']);
else
frameDur = 1.0 / 60.0; // couldn't get a reliable measure so guess
// add info from the URL:
util.addInfoFromUrl(expInfo);
return Scheduler.Event.NEXT;
}
var instructClock;
var ready;
var gaborClock;
var gabor;
var stims = [];
window.grating2BlendMode = 'add';
var globalClock;
var routineTimer;
function addWheelListener () {
let v = 1.;
window.addEventListener('wheel', (e) => {
if (!psychoJS) {
return;
}
psychoJS._window._stimsContainer.position.y += e.deltaY * v;
})
}
// var video;
async function experimentInit() {
// Initialize components for Routine "instruct"
instructClock = new util.Clock();
ready = new core.Keyboard({psychoJS: psychoJS, clock: new util.Clock(), waitForStart: true});
psychoJS.window.backgroundImage = "starformation.jpg";
// Initialize components for Routine "gabor"
gaborClock = new util.Clock();
stims.push(
// new visual.GratingStim({
// win : psychoJS.window,
// name: 'morph',
// tex: 'sin',
// mask: undefined,
// ori: 0,
// size: [256, 512],
// pos: [0, 0],
// units: "pix",
// depth: 0
// })
new visual.GifStim({
win : psychoJS.window,
name: 'morph',
image: "silverhand.gif",
mask: undefined,
ori: 0,
size: [512, 512],
pos: [0, 0],
units: "pix",
depth: 0
})
);
window.stims = stims;
// Create some handy timers
globalClock = new util.Clock(); // to track the time since experiment started
routineTimer = new util.CountdownTimer(); // to track time remaining of each (non-slip) routine
addWheelListener();
return Scheduler.Event.NEXT;
}
var t;
var frameN;
var continueRoutine;
var gotValidClick;
var _ready_allKeys;
var instructComponents;
function instructRoutineBegin(snapshot) {
return async function () {
TrialHandler.fromSnapshot(snapshot); // ensure that .thisN vals are up to date
//------Prepare to start Routine 'instruct'-------
t = 0;
instructClock.reset(); // clock
frameN = -1;
continueRoutine = true; // until we're told otherwise
// update component parameters for each repeat
ready.keys = undefined;
ready.rt = undefined;
_ready_allKeys = [];
// keep track of which components have finished
instructComponents = [];
instructComponents.push(ready);
for (const thisComponent of instructComponents)
if ('status' in thisComponent)
thisComponent.status = PsychoJS.Status.NOT_STARTED;
return Scheduler.Event.NEXT;
}
}
function instructRoutineEachFrame() {
return async function () {
//------Loop for each frame of Routine 'instruct'-------
// get current time
t = instructClock.getTime();
frameN = frameN + 1;// number of completed frames (so 0 is the first frame)
// update/draw components on each frame
// *ready* updates
if (t >= 0 && ready.status === PsychoJS.Status.NOT_STARTED) {
// keep track of start time/frame for later
ready.tStart = t; // (not accounting for frame time here)
ready.frameNStart = frameN; // exact frame index
// keyboard checking is just starting
psychoJS.window.callOnFlip(function() { ready.clock.reset(); }); // t=0 on next screen flip
psychoJS.window.callOnFlip(function() { ready.start(); }); // start on screen flip
psychoJS.window.callOnFlip(function() { ready.clearEvents(); });
}
if (ready.status === PsychoJS.Status.STARTED) {
let theseKeys = ready.getKeys({keyList: [], waitRelease: false});
_ready_allKeys = _ready_allKeys.concat(theseKeys);
if (_ready_allKeys.length > 0) {
ready.keys = _ready_allKeys[_ready_allKeys.length - 1].name; // just the last key pressed
ready.rt = _ready_allKeys[_ready_allKeys.length - 1].rt;
// a response ends the routine
continueRoutine = false;
}
}
// check for quit (typically the Esc key)
if (psychoJS.experiment.experimentEnded || psychoJS.eventManager.getKeys({keyList:['escape']}).length > 0) {
return quitPsychoJS('The [Escape] key was pressed. Goodbye!', false);
}
// check if the Routine should terminate
if (!continueRoutine) { // a component has requested a forced-end of Routine
return Scheduler.Event.NEXT;
}
continueRoutine = false; // reverts to True if at least one component still running
for (const thisComponent of instructComponents)
if ('status' in thisComponent && thisComponent.status !== PsychoJS.Status.FINISHED) {
continueRoutine = true;
break;
}
// refresh the screen if continuing
if (continueRoutine) {
return Scheduler.Event.FLIP_REPEAT;
} else {
return Scheduler.Event.NEXT;
}
};
}
function instructRoutineEnd() {
return async function () {
//------Ending Routine 'instruct'-------
for (const thisComponent of instructComponents) {
if (typeof thisComponent.setAutoDraw === 'function') {
thisComponent.setAutoDraw(false);
}
}
ready.stop();
// the Routine "instruct" was not non-slip safe, so reset the non-slip timer
routineTimer.reset();
return Scheduler.Event.NEXT;
};
}
var gaborComponents;
function gaborRoutineBegin(snapshot) {
return async function () {
TrialHandler.fromSnapshot(snapshot); // ensure that .thisN vals are up to date
//------Prepare to start Routine 'instruct'-------
t = 0;
gaborClock.reset(); // clock
frameN = -1;
continueRoutine = true; // until we're told otherwise
// update component parameters for each repeat
ready.keys = undefined;
ready.rt = undefined;
_ready_allKeys = [];
// keep track of which components have finished
gaborComponents = [];
gaborComponents.push(ready);
gaborComponents = [...gaborComponents, ...stims];
for (const thisComponent of gaborComponents)
if ('status' in thisComponent)
thisComponent.status = PsychoJS.Status.NOT_STARTED;
return Scheduler.Event.NEXT;
}
}
var secTimer = 0;
var prevTime = performance.now();
var dynamicDimension = 0;
var newSize = [512, 512];
var newPos = [0, 0];
var sizeTests = [-512, -256.1, -128, 128, 256.6, 512];
var positionTests = [-256, -256.1, 256, 256.1, 0];
var anchorTests = ["left", "topleft", "top", "topright", "right", "bottomright", "bottom", "bottomleft", "center"];
var sizeTestsProgress = 0;
var positionTestsProgress = 0;
var anchorTestsProgress = 0;
var continueAutoTest = true;
window.stopTest = function () {
continueAutoTest = false;
};
window.startTest = function () {
continueAutoTest = true;
};
function gaborRoutineEachFrame() {
return async function () {
//------Loop for each frame of Routine 'gabor'-------
// get current time
t = gaborClock.getTime();
frameN = frameN + 1;// number of completed frames (so 0 is the first frame)
let i;
for (i = 0; i < stims.length; i++) {
if (t >= 0. && stims[i].status === PsychoJS.Status.NOT_STARTED) {
stims[i].tStart = t;
stims[i].frameNStart = frameN;
stims[i].setAutoDraw(true);
}
}
// testing code
secTimer += performance.now() - prevTime;
prevTime = performance.now();
if (secTimer >= 1000 && continueAutoTest)
{
secTimer = 0;
if (sizeTestsProgress < sizeTests.length * 2)
{
i = sizeTestsProgress % sizeTests.length;
newSize[dynamicDimension] = sizeTests[i];
stims[0].setSize(newSize);
sizeTestsProgress++;
console.log("stim size set to", stims[0].getSize());
if (sizeTestsProgress % sizeTests.length === 0)
{
dynamicDimension = (dynamicDimension + 1) % 2;
}
}
else if (sizeTestsProgress < sizeTests.length * 3)
{
i = sizeTestsProgress % sizeTests.length;
newSize[0] = sizeTests[i];
newSize[1] = sizeTests[i];
stims[0].setSize(newSize);
sizeTestsProgress++;
console.log("stim size set to", stims[0].getSize());
}
else if (
sizeTestsProgress >= sizeTests.length * 3 &&
positionTestsProgress < positionTests.length * 2)
{
i = positionTestsProgress % positionTests.length;
newPos[dynamicDimension] = positionTests[i];
stims[0].setPos(newPos);
positionTestsProgress++;
console.log("stim pos set to", stims[0].getPos());
if (positionTestsProgress % positionTests.length === 0)
{
newPos[dynamicDimension] = 0;
dynamicDimension = (dynamicDimension + 1) % 2;
}
}
else if(
sizeTestsProgress >= sizeTests.length * 3 &&
positionTestsProgress >= positionTests.length * 2 &&
anchorTestsProgress < anchorTests.length)
{
i = anchorTestsProgress % anchorTests.length;
stims[0].setAnchor(anchorTests[i]);
anchorTestsProgress++;
console.log("anchor set to", anchorTests[i]);
}
if (
sizeTestsProgress >= sizeTests.length * 3 &&
positionTestsProgress >= positionTests.length * 2 &&
anchorTestsProgress >= anchorTests.length)
{
sizeTestsProgress = 0;
positionTestsProgress = 0;
anchorTestsProgress = 0;
dynamicDimension = 0;
newPos[0] = 0;
newPos[1] = 0;
newSize[0] = 512;
newSize[1] = 512;
console.log("============== full reset ==============");
stims[0].setPos(newPos);
stims[0].setSize(newSize);
stims[0].setAnchor("center");
}
}
// check for quit (typically the Esc key)
if (psychoJS.experiment.experimentEnded || psychoJS.eventManager.getKeys({keyList:['escape']}).length > 0)
{
continueRoutine = false;
}
// check if the Routine should terminate
if (!continueRoutine) { // a component has requested a forced-end of Routine
return Scheduler.Event.NEXT;
}
continueRoutine = false; // reverts to True if at least one component still running
for (const thisComponent of gaborComponents)
if ('status' in thisComponent && thisComponent.status !== PsychoJS.Status.FINISHED) {
continueRoutine = true;
break;
}
// refresh the screen if continuing
if (continueRoutine) {
return Scheduler.Event.FLIP_REPEAT;
} else {
return Scheduler.Event.NEXT;
}
};
}
function gaborRoutineEnd() {
return async function () {
//------Ending Routine 'gabor'-------
for (const thisComponent of gaborComponents) {
if (typeof thisComponent.setAutoDraw === 'function') {
thisComponent.setAutoDraw(false);
}
}
// the Routine "gabor" was not non-slip safe, so reset the non-slip timer
routineTimer.reset();
return Scheduler.Event.NEXT;
};
}
function endLoopIteration(scheduler, snapshot) {
// ------Prepare for next entry------
return async function () {
if (typeof snapshot !== 'undefined') {
// ------Check if user ended loop early------
if (snapshot.finished) {
// Check for and save orphaned data
if (psychoJS.experiment.isEntryEmpty()) {
psychoJS.experiment.nextEntry(snapshot);
}
scheduler.stop();
} else {
const thisTrial = snapshot.getCurrentTrial();
if (typeof thisTrial === 'undefined' || !('isTrials' in thisTrial) || thisTrial.isTrials) {
psychoJS.experiment.nextEntry(snapshot);
}
}
return Scheduler.Event.NEXT;
}
};
}
function importConditions(currentLoop) {
return async function () {
psychoJS.importAttributes(currentLoop.getCurrentTrial());
return Scheduler.Event.NEXT;
};
}
async function quitPsychoJS(message, isCompleted) {
// Check for and save orphaned data
if (psychoJS.experiment.isEntryEmpty()) {
psychoJS.experiment.nextEntry();
}
psychoJS.window.close();
psychoJS.quit({message: message, isCompleted: isCompleted});
return Scheduler.Event.QUIT;
}