mirror of
https://github.com/psychopy/psychojs.git
synced 2025-05-10 10:40:54 +00:00
dot stim beginngins
This commit is contained in:
parent
a89346dd14
commit
9308bacbfd
@ -20,33 +20,35 @@ const { round } = util;
|
||||
let expName = 'gabor'; // from the Builder filename that created this script
|
||||
let expInfo = {};
|
||||
|
||||
const TESTING = false;
|
||||
|
||||
// Start code blocks for 'Before Experiment'
|
||||
// init psychoJS:
|
||||
const psychoJS = new PsychoJS({
|
||||
debug: true
|
||||
debug: true
|
||||
});
|
||||
window.psychoJS = psychoJS;
|
||||
window.util = util;
|
||||
|
||||
// open window:
|
||||
psychoJS.openWindow({
|
||||
fullscr: false,
|
||||
color: new util.Color("gray"),
|
||||
units: 'height',
|
||||
waitBlanking: true
|
||||
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
|
||||
}));
|
||||
// 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);
|
||||
// psychoJS.scheduleCondition(function() { return (psychoJS.gui.dialogComponent.button === 'OK'); }, flowScheduler, dialogCancelScheduler);
|
||||
|
||||
// flowScheduler gets run if the participants presses OK
|
||||
flowScheduler.add(updateInfo); // add timeStamp
|
||||
@ -59,41 +61,43 @@ flowScheduler.add(gaborRoutineEachFrame());
|
||||
flowScheduler.add(gaborRoutineEnd());
|
||||
flowScheduler.add(quitPsychoJS, '', true);
|
||||
|
||||
flowScheduler.start();
|
||||
|
||||
// quit if user presses Cancel in dialog box:
|
||||
dialogCancelScheduler.add(quitPsychoJS, '', false);
|
||||
|
||||
psychoJS.start({
|
||||
expName: expName,
|
||||
expInfo: expInfo,
|
||||
configURL: "../config.json",
|
||||
resources: [
|
||||
// {
|
||||
// name: "007",
|
||||
// path: "007.jpg"
|
||||
// },
|
||||
]
|
||||
expName: expName,
|
||||
expInfo: expInfo,
|
||||
configURL: "../config.json",
|
||||
resources: [
|
||||
// {
|
||||
// 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;
|
||||
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
|
||||
// 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);
|
||||
// add info from the URL:
|
||||
util.addInfoFromUrl(expInfo);
|
||||
|
||||
return Scheduler.Event.NEXT;
|
||||
return Scheduler.Event.NEXT;
|
||||
}
|
||||
|
||||
var instructClock;
|
||||
@ -106,46 +110,56 @@ var globalClock;
|
||||
var routineTimer;
|
||||
|
||||
function addWheelListener () {
|
||||
let v = 1.;
|
||||
window.addEventListener('wheel', (e) => {
|
||||
if (!psychoJS) {
|
||||
return;
|
||||
}
|
||||
psychoJS._window._stimsContainer.position.y += e.deltaY * v;
|
||||
})
|
||||
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 = "toxen";
|
||||
// Initialize components for Routine "instruct"
|
||||
instructClock = new util.Clock();
|
||||
ready = new core.Keyboard({psychoJS: psychoJS, clock: new util.Clock(), waitForStart: true});
|
||||
psychoJS.window.backgroundImage = "toxen";
|
||||
|
||||
// Initialize components for Routine "gabor"
|
||||
gaborClock = new util.Clock();
|
||||
// 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
|
||||
})
|
||||
);
|
||||
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.DotStim({
|
||||
win : psychoJS.window,
|
||||
name: 'dots',
|
||||
nDots: 100,
|
||||
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();
|
||||
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;
|
||||
return Scheduler.Event.NEXT;
|
||||
}
|
||||
|
||||
|
||||
@ -156,131 +170,131 @@ var gotValidClick;
|
||||
var _ready_allKeys;
|
||||
var instructComponents;
|
||||
function instructRoutineBegin(snapshot) {
|
||||
return async function () {
|
||||
TrialHandler.fromSnapshot(snapshot); // ensure that .thisN vals are up to date
|
||||
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);
|
||||
//------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;
|
||||
}
|
||||
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
|
||||
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
|
||||
// *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(); });
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
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 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;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
};
|
||||
// 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 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;
|
||||
};
|
||||
return Scheduler.Event.NEXT;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
var gaborComponents;
|
||||
function gaborRoutineBegin(snapshot) {
|
||||
return async function () {
|
||||
TrialHandler.fromSnapshot(snapshot); // ensure that .thisN vals are up to date
|
||||
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];
|
||||
//------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;
|
||||
for (const thisComponent of gaborComponents)
|
||||
if ('status' in thisComponent)
|
||||
thisComponent.status = PsychoJS.Status.NOT_STARTED;
|
||||
|
||||
return Scheduler.Event.NEXT;
|
||||
}
|
||||
return Scheduler.Event.NEXT;
|
||||
}
|
||||
}
|
||||
|
||||
var secTimer = 0;
|
||||
@ -296,188 +310,190 @@ var positionTestsProgress = 0;
|
||||
var anchorTestsProgress = 0;
|
||||
var continueAutoTest = true;
|
||||
window.stopTest = function () {
|
||||
continueAutoTest = false;
|
||||
continueAutoTest = false;
|
||||
};
|
||||
window.startTest = function () {
|
||||
continueAutoTest = true;
|
||||
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)
|
||||
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);
|
||||
}
|
||||
}
|
||||
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 (TESTING)
|
||||
{
|
||||
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 * 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");
|
||||
}
|
||||
}
|
||||
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 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;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
};
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
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();
|
||||
// the Routine "gabor" was not non-slip safe, so reset the non-slip timer
|
||||
routineTimer.reset();
|
||||
|
||||
return Scheduler.Event.NEXT;
|
||||
};
|
||||
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;
|
||||
}
|
||||
};
|
||||
// ------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;
|
||||
};
|
||||
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});
|
||||
// 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;
|
||||
return Scheduler.Event.QUIT;
|
||||
}
|
||||
|
394
src/visual/DotStim.js
Normal file
394
src/visual/DotStim.js
Normal file
@ -0,0 +1,394 @@
|
||||
/**
|
||||
* Dot Stimulus.
|
||||
*
|
||||
* @author Nikita Agafonov
|
||||
* @license Distributed under the terms of the MIT License
|
||||
*/
|
||||
|
||||
import * as PIXI from "pixi.js-legacy";
|
||||
import {AdjustmentFilter} from "@pixi/filter-adjustment";
|
||||
import { Color } from "../util/Color.js";
|
||||
import { to_pixiPoint } from "../util/Pixi.js";
|
||||
import * as util from "../util/Util.js";
|
||||
import { VisualStim } from "./VisualStim.js";
|
||||
|
||||
/**
|
||||
* Grating Stimulus.
|
||||
*
|
||||
* @extends VisualStim
|
||||
*/
|
||||
export class DotStim extends VisualStim
|
||||
{
|
||||
/**
|
||||
* Default size of the Dot Stimuli in pixels.
|
||||
*
|
||||
* @type {Array}
|
||||
* @default [256, 256]
|
||||
*/
|
||||
static #DEFAULT_STIM_SIZE_PX = [256, 256]; // in pixels
|
||||
|
||||
static #BLEND_MODES_MAP = {
|
||||
avg: PIXI.BLEND_MODES.NORMAL,
|
||||
add: PIXI.BLEND_MODES.ADD,
|
||||
mul: PIXI.BLEND_MODES.MULTIPLY,
|
||||
screen: PIXI.BLEND_MODES.SCREEN
|
||||
};
|
||||
|
||||
/**
|
||||
* @memberOf module:visual
|
||||
* @param {Object} options
|
||||
* @param {String} options.name - the name used when logging messages from this stimulus
|
||||
* @param {Window} options.win - the associated Window
|
||||
* @param {String | HTMLImageElement} [options.tex="sin"] - the name of the predefined grating texture or image resource or the HTMLImageElement corresponding to the texture
|
||||
* @param {String | HTMLImageElement} [options.mask] - the name of the mask resource or HTMLImageElement corresponding to the mask
|
||||
* @param {String} [options.units= "norm"] - the units of the stimulus (e.g. for size, position, vertices)
|
||||
* @param {number} [options.sf=1.0] - spatial frequency of the function used in grating stimulus
|
||||
* @param {number} [options.phase=0.0] - phase of the function used in grating stimulus, multiples of period of that function
|
||||
* @param {Array.<number>} [options.pos= [0, 0]] - the position of the center of the stimulus
|
||||
* @param {string} [options.anchor = "center"] - sets the origin point of the stim
|
||||
* @param {number} [options.ori= 0.0] - the orientation (in degrees)
|
||||
* @param {number} [options.size] - the size of the rendered image (DEFAULT_STIM_SIZE_PX will be used if size is not specified)
|
||||
* @param {Color} [options.color= "white"] - Foreground color of the stimulus. Can be String like "red" or "#ff0000" or Number like 0xff0000.
|
||||
* @param {number} [options.opacity= 1.0] - Set the opacity of the stimulus. Determines how visible the stimulus is relative to background.
|
||||
* @param {number} [options.contrast= 1.0] - Set the contrast of the stimulus, i.e. scales how far the stimulus deviates from the middle grey. Ranges [-1, 1].
|
||||
* @param {number} [options.depth= 0] - the depth (i.e. the z order)
|
||||
* @param {boolean} [options.interpolate= false] - Whether to interpolate (linearly) the texture in the stimulus. Currently supports only image based gratings.
|
||||
* @param {String} [options.blendmode= "avg"] - blend mode of the stimulus, determines how the stimulus is blended with the background. Supported values: "avg", "add", "mul", "screen".
|
||||
* @param {boolean} [options.autoDraw= false] - whether or not the stimulus should be automatically drawn on every frame flip
|
||||
* @param {boolean} [options.autoLog= false] - whether or not to log
|
||||
*/
|
||||
constructor({
|
||||
name,
|
||||
win,
|
||||
units,
|
||||
nDots = 1,
|
||||
coherence = 0.5,
|
||||
pos = [0, 0],
|
||||
size = [ 1, 1 ],
|
||||
fieldShape = "sqr",
|
||||
dotSize = 2,
|
||||
dotLife = 3,
|
||||
dir = 0.0,
|
||||
speed = 0.5,
|
||||
signalDots = "same",
|
||||
noiseDots = "direction",
|
||||
anchor,
|
||||
ori,
|
||||
color,
|
||||
colorSpace,
|
||||
opacity,
|
||||
contrast = 1,
|
||||
depth,
|
||||
interpolate,
|
||||
blendmode,
|
||||
autoDraw,
|
||||
autoLog,
|
||||
} = {})
|
||||
{
|
||||
super({ name, win, units, ori, opacity, depth, pos, anchor, size, autoDraw, autoLog });
|
||||
|
||||
this._pixi = new PIXI.Container();
|
||||
this._adjustmentFilter = new AdjustmentFilter({ contrast });
|
||||
|
||||
this._addAttribute("coherence", coherence, coherence);
|
||||
this._addAttribute("nDots", nDots, nDots);
|
||||
this._addAttribute("fieldShape", fieldShape, fieldShape);
|
||||
this._addAttribute("dotSize", dotSize, dotSize);
|
||||
this._addAttribute("dotLife", dotLife, dotLife);
|
||||
this._addAttribute("speed", speed, speed);
|
||||
this._addAttribute("dir", dir, dir);
|
||||
this._addAttribute("signalDots", signalDots, signalDots);
|
||||
this._addAttribute("noiseDots", noiseDots, noiseDots);
|
||||
this._addAttribute("color", color, "white");
|
||||
this._addAttribute("colorSpace", colorSpace, "RGB");
|
||||
this._addAttribute("contrast",
|
||||
contrast,
|
||||
1.0,
|
||||
() => { this._adjustmentFilter.contrast = this._contrast; }
|
||||
);
|
||||
this._addAttribute("blendmode", blendmode, "avg");
|
||||
this._addAttribute("interpolate", interpolate, false);
|
||||
|
||||
// estimate the bounding box:
|
||||
this._estimateBoundingBox();
|
||||
|
||||
if (this._autoLog)
|
||||
{
|
||||
this._psychoJS.experimentLogger.exp(`Created ${this.name} = ${this.toString()}`);
|
||||
}
|
||||
|
||||
if (!Array.isArray(this.size) || this.size.length === 0)
|
||||
{
|
||||
this.size = util.to_unit(DotStim.#DEFAULT_STIM_SIZE_PX, "pix", this.win, this.units);
|
||||
}
|
||||
|
||||
this._size_px = util.to_px(this.size, this.units, this.win);
|
||||
this._spawnDots();
|
||||
}
|
||||
|
||||
_getRandomPositionWithinSquareField(fieldWidth, fieldHeight)
|
||||
{
|
||||
const x = Math.random() * fieldWidth;
|
||||
const y = Math.random() * fieldHeight;
|
||||
|
||||
return { x, y };
|
||||
}
|
||||
|
||||
_getRandomPosWithinCircleField(fieldRadius)
|
||||
{
|
||||
const f = Math.random();
|
||||
const x = Math.cos(f) * fieldRadius;
|
||||
const y = Math.sin(f) * fieldRadius;
|
||||
|
||||
return { x, y };
|
||||
}
|
||||
|
||||
_spawnDots()
|
||||
{
|
||||
let i;
|
||||
let p;
|
||||
let dot;
|
||||
|
||||
for (i = 0; i < this._nDots; i ++)
|
||||
{
|
||||
p = this._getRandomPositionWithinSquareField(this._size_px[0], this._size_px[1]);
|
||||
dot = new PIXI.Sprite(PIXI.Texture.WHITE);
|
||||
dot.x = p.x;
|
||||
dot.y = p.y;
|
||||
dot.width = this._dotSize;
|
||||
dot.height = this._dotSize;
|
||||
this._pixi.addChild(dot);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the mask attribute.
|
||||
*
|
||||
* @param {HTMLImageElement | string} mask - the name of the mask resource or HTMLImageElement corresponding to the mask
|
||||
* @param {boolean} [log= false] - whether of not to log
|
||||
*/
|
||||
setMask(mask, log = false)
|
||||
{
|
||||
const response = {
|
||||
origin: "DotStim.setMask",
|
||||
context: "when setting the mask of DotStim: " + this._name,
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
// mask is undefined: that's fine but we raise a warning in case this is a sympton of an actual problem
|
||||
if (typeof mask === "undefined")
|
||||
{
|
||||
this.psychoJS.logger.warn("setting the mask of DotStim: " + this._name + " with argument: undefined.");
|
||||
this.psychoJS.logger.debug("set the mask of DotStim: " + this._name + " as: undefined");
|
||||
}
|
||||
else
|
||||
{
|
||||
// mask is a string: it should be the name of a resource, which we load
|
||||
if (typeof mask === "string")
|
||||
{
|
||||
mask = this.psychoJS.serverManager.getResource(mask);
|
||||
}
|
||||
|
||||
// mask should now be an actual HTMLImageElement: we raise an error if it is not
|
||||
if (!(mask instanceof HTMLImageElement))
|
||||
{
|
||||
throw "the argument: " + mask.toString() + " is not an image\" }";
|
||||
}
|
||||
|
||||
this.psychoJS.logger.debug("set the mask of DotStim: " + this._name + " as: src= " + mask.src + ", size= " + mask.width + "x" + mask.height);
|
||||
}
|
||||
|
||||
this._setAttribute("mask", mask, log);
|
||||
|
||||
this._onChange(true, false)();
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
throw Object.assign(response, { error });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of the display image, which is either that of the DotStim or that of the image
|
||||
* it contains.
|
||||
*
|
||||
* @protected
|
||||
* @return {number[]} the size of the displayed image
|
||||
*/
|
||||
_getDisplaySize()
|
||||
{
|
||||
let displaySize = this._size;
|
||||
|
||||
if (typeof displaySize === "undefined")
|
||||
{
|
||||
// use the size of the pixi element, if we have access to it:
|
||||
if (typeof this._pixi !== "undefined" && this._pixi.width > 0)
|
||||
{
|
||||
const pixiContainerSize = [this._pixi.width, this._pixi.height];
|
||||
displaySize = util.to_unit(pixiContainerSize, "pix", this.win, this.units);
|
||||
}
|
||||
}
|
||||
|
||||
return displaySize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate the bounding box.
|
||||
*
|
||||
* @override
|
||||
* @protected
|
||||
*/
|
||||
_estimateBoundingBox()
|
||||
{
|
||||
const size = this._getDisplaySize();
|
||||
if (typeof size !== "undefined")
|
||||
{
|
||||
this._boundingBox = new PIXI.Rectangle(
|
||||
this._pos[0] - size[0] / 2,
|
||||
this._pos[1] - size[1] / 2,
|
||||
size[0],
|
||||
size[1],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set color space value for the grating stimulus.
|
||||
*
|
||||
* @param {String} colorSpaceVal - color space value
|
||||
* @param {boolean} [log= false] - whether of not to log
|
||||
*/
|
||||
setColorSpace (colorSpaceVal = "RGB", log = false)
|
||||
{
|
||||
let colorSpaceValU = colorSpaceVal.toUpperCase();
|
||||
if (Color.COLOR_SPACE[colorSpaceValU] === undefined)
|
||||
{
|
||||
colorSpaceValU = "RGB";
|
||||
}
|
||||
const hasChanged = this._setAttribute("colorSpace", colorSpaceValU, log);
|
||||
if (hasChanged)
|
||||
{
|
||||
this.setColor(this._color);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set foreground color value for the grating stimulus.
|
||||
*
|
||||
* @param {Color} colorVal - color value, can be String like "red" or "#ff0000" or Number like 0xff0000.
|
||||
* @param {boolean} [log= false] - whether of not to log
|
||||
*/
|
||||
setColor (colorVal = "white", log = false)
|
||||
{
|
||||
const colorObj = (colorVal instanceof Color) ? colorVal : new Color(colorVal, Color.COLOR_SPACE[this._colorSpace])
|
||||
this._setAttribute("color", colorObj, log);
|
||||
// TODO: update dots?
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines how visible the stimulus is relative to background.
|
||||
*
|
||||
* @param {number} [opacity=1] opacity - The value should be a single float ranging 1.0 (opaque) to 0.0 (transparent).
|
||||
* @param {boolean} [log= false] - whether of not to log
|
||||
*/
|
||||
setOpacity (opacity = 1, log = false)
|
||||
{
|
||||
this._setAttribute("opacity", opacity, log);
|
||||
if (this._pixi)
|
||||
{
|
||||
this._pixi.opacity = opacity;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set blend mode of the grating stimulus.
|
||||
*
|
||||
* @param {String} blendMode - blend mode, can be one of the following: ["avg", "add", "mul", "screen"].
|
||||
* @param {boolean} [log=false] - whether or not to log
|
||||
*/
|
||||
setBlendmode (blendMode = "avg", log = false)
|
||||
{
|
||||
this._setAttribute("blendmode", blendMode, log);
|
||||
if (this._pixi !== undefined)
|
||||
{
|
||||
let pixiBlendMode = DotStim.#BLEND_MODES_MAP[blendMode];
|
||||
if (pixiBlendMode === undefined)
|
||||
{
|
||||
pixiBlendMode = PIXI.BLEND_MODES.NORMAL;
|
||||
}
|
||||
if (this._pixi.filters)
|
||||
{
|
||||
this._pixi.filters[this._pixi.filters.length - 1].blendMode = pixiBlendMode;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._pixi.blendMode = pixiBlendMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to interpolate (linearly) the texture in the stimulus.
|
||||
*
|
||||
* @param {boolean} interpolate - interpolate or not.
|
||||
* @param {boolean} [log=false] - whether or not to log
|
||||
*/
|
||||
setInterpolate (interpolate = false, log = false)
|
||||
{
|
||||
this._setAttribute("interpolate", interpolate, log);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the anchor attribute.
|
||||
*
|
||||
* @param {string} anchor - anchor of the stim
|
||||
* @param {boolean} [log= false] - whether or not to log
|
||||
*/
|
||||
setAnchor (anchor = "center", log = false)
|
||||
{
|
||||
this._setAttribute("anchor", anchor, log);
|
||||
if (this._pixi !== undefined)
|
||||
{
|
||||
const anchorNum = this._anchorTextToNum(this._anchor);
|
||||
this._pixi.pivot.x = anchorNum[0] * this._pixi.scale.x * this._pixi.width;
|
||||
this._pixi.pivot.y = anchorNum[1] * this._pixi.scale.y * this._pixi.height;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the stimulus, if necessary.
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
_updateIfNeeded()
|
||||
{
|
||||
if (!this._needUpdate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
this._needUpdate = false;
|
||||
|
||||
// update the PIXI representation, if need be:
|
||||
if (this._needPixiUpdate)
|
||||
{
|
||||
this._needPixiUpdate = false;
|
||||
this._size_px = util.to_px(this._size, this.units, this.win);
|
||||
}
|
||||
|
||||
this._pixi.zIndex = -this._depth;
|
||||
this.opacity = this._opacity;
|
||||
this.anchor = this._anchor;
|
||||
|
||||
// set the scale:
|
||||
this._pixi.scale.x = 1;
|
||||
this._pixi.scale.y = -1;
|
||||
|
||||
let pos = to_pixiPoint(this.pos, this.units, this.win);
|
||||
this._pixi.position.set(pos.x, pos.y);
|
||||
this._pixi.rotation = -this.ori * Math.PI / 180;
|
||||
|
||||
// re-estimate the bounding box, as the texture's width may now be available:
|
||||
this._estimateBoundingBox();
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ export * from "./ButtonStim.js";
|
||||
export * from "./Form.js";
|
||||
export * from "./ImageStim.js";
|
||||
export * from "./GratingStim.js";
|
||||
export * from "./DotStim.js";
|
||||
export * from "./MovieStim.js";
|
||||
export * from "./Polygon.js";
|
||||
export * from "./Rect.js";
|
||||
@ -14,4 +15,4 @@ export * from "./VisualStim.js";
|
||||
export * from "./FaceDetector.js";
|
||||
export * from "./Survey.js";
|
||||
export * from "./ParticleEmitter.js";
|
||||
export * from "./Progress.js";
|
||||
export * from "./Progress.js";
|
||||
|
@ -5,6 +5,9 @@ const fileName = `psychojs-${process.env.npm_package_version}`;
|
||||
export default {
|
||||
root: "./src/",
|
||||
base: "./",
|
||||
define: {
|
||||
PSYCHOJS_VERSION: JSON.stringify(process.env.npm_package_version)
|
||||
},
|
||||
build:
|
||||
{
|
||||
outDir: "../out",
|
||||
|
Loading…
Reference in New Issue
Block a user