mirror of
https://github.com/psychopy/psychojs.git
synced 2025-05-10 18:50:54 +00:00
added spatial frequency and phase support for image based grating stims;
This commit is contained in:
parent
120060cbab
commit
88b7345535
@ -31,6 +31,8 @@ const DEFINED_FUNCTIONS = {
|
|||||||
raisedCos: undefined
|
raisedCos: undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DEFAULT_STIM_SIZE = [256, 256]; // in pixels
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Grating Stimulus.
|
* Grating Stimulus.
|
||||||
*
|
*
|
||||||
@ -45,7 +47,6 @@ const DEFINED_FUNCTIONS = {
|
|||||||
* @param {string | HTMLImageElement} options.mask - the name of the mask resource or HTMLImageElement corresponding to the mask
|
* @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 {string} [options.units= "norm"] - the units of the stimulus (e.g. for size, position, vertices)
|
||||||
* @param {Array.<number>} [options.pos= [0, 0]] - the position of the center of the stimulus
|
* @param {Array.<number>} [options.pos= [0, 0]] - the position of the center of the stimulus
|
||||||
* @param {string} [options.units= 'norm'] - the units of the stimulus vertices, size and position
|
|
||||||
* @param {number} [options.ori= 0.0] - the orientation (in degrees)
|
* @param {number} [options.ori= 0.0] - the orientation (in degrees)
|
||||||
* @param {number} [options.size] - the size of the rendered image (the size of the image will be used if size is not specified)
|
* @param {number} [options.size] - the size of the rendered image (the size of the image will be used if size is not specified)
|
||||||
* @param {Color} [options.color= 'white'] the background color
|
* @param {Color} [options.color= 'white'] the background color
|
||||||
@ -126,14 +127,12 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
|
|||||||
this._addAttribute(
|
this._addAttribute(
|
||||||
"spatialFrequency",
|
"spatialFrequency",
|
||||||
spatialFrequency,
|
spatialFrequency,
|
||||||
10.,
|
10.
|
||||||
this._onChange(true, false)
|
|
||||||
);
|
);
|
||||||
this._addAttribute(
|
this._addAttribute(
|
||||||
"phase",
|
"phase",
|
||||||
phase,
|
phase,
|
||||||
0.,
|
0.
|
||||||
this._onChange(true, false)
|
|
||||||
);
|
);
|
||||||
this._addAttribute(
|
this._addAttribute(
|
||||||
"color",
|
"color",
|
||||||
@ -167,6 +166,11 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
|
|||||||
{
|
{
|
||||||
this._psychoJS.experimentLogger.exp(`Created ${this.name} = ${this.toString()}`);
|
this._psychoJS.experimentLogger.exp(`Created ${this.name} = ${this.toString()}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(this.size) || this.size.length === 0) {
|
||||||
|
this.size = util.to_unit(DEFAULT_STIM_SIZE, "pix", this.win, this.units);
|
||||||
|
}
|
||||||
|
this._sizeInPixels = util.to_px(this.size, this.units, this.win);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -198,8 +202,8 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
|
|||||||
{
|
{
|
||||||
// tex is a string and it is one of predefined functions available in shaders
|
// tex is a string and it is one of predefined functions available in shaders
|
||||||
this.psychoJS.logger.debug("the tex is one of predefined functions. Set the tex of GratingStim: " + this._name + " as: " + tex);
|
this.psychoJS.logger.debug("the tex is one of predefined functions. Set the tex of GratingStim: " + this._name + " as: " + tex);
|
||||||
const existingImage = this.getTex();
|
const curFuncName = this.getTex();
|
||||||
hasChanged = existingImage ? existingImage !== tex : true;
|
hasChanged = curFuncName ? curFuncName !== tex : true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -289,10 +293,10 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the size of the display image, which is either that of the ImageStim or that of the image
|
* Get the size of the display image, which is either that of the GratingStim or that of the image
|
||||||
* it contains.
|
* it contains.
|
||||||
*
|
*
|
||||||
* @name module:visual.ImageStim#_getDisplaySize
|
* @name module:visual.GratingStim#_getDisplaySize
|
||||||
* @private
|
* @private
|
||||||
* @return {number[]} the size of the displayed image
|
* @return {number[]} the size of the displayed image
|
||||||
*/
|
*/
|
||||||
@ -316,7 +320,7 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
|
|||||||
/**
|
/**
|
||||||
* Estimate the bounding box.
|
* Estimate the bounding box.
|
||||||
*
|
*
|
||||||
* @name module:visual.ImageStim#_estimateBoundingBox
|
* @name module:visual.GratingStim#_estimateBoundingBox
|
||||||
* @function
|
* @function
|
||||||
* @override
|
* @override
|
||||||
* @protected
|
* @protected
|
||||||
@ -343,9 +347,9 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
|
|||||||
'aVertexPosition',
|
'aVertexPosition',
|
||||||
[
|
[
|
||||||
0, 0,
|
0, 0,
|
||||||
256, 0,
|
this._sizeInPixels[0], 0,
|
||||||
256, 256,
|
this._sizeInPixels[0], this._sizeInPixels[1],
|
||||||
0, 256
|
0, this._sizeInPixels[1]
|
||||||
],
|
],
|
||||||
2
|
2
|
||||||
);
|
);
|
||||||
@ -364,10 +368,30 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
|
|||||||
return new PIXI.Mesh(geometry, shader);
|
return new PIXI.Mesh(geometry, shader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPhase (phase, log = false) {
|
||||||
|
this._setAttribute("phase", phase, log);
|
||||||
|
if (this._pixi instanceof PIXI.Mesh) {
|
||||||
|
this._pixi.shader.uniforms.uPhase = phase;
|
||||||
|
} else if (this._pixi instanceof PIXI.TilingSprite) {
|
||||||
|
this._pixi.tilePosition.x = -phase * (this._sizeInPixels[0] * this._pixi.tileScale.x) / (2 * Math.PI)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setSpatialFrequency (sf, log = false) {
|
||||||
|
this._setAttribute("spatialFrequency", sf, log);
|
||||||
|
if (this._pixi instanceof PIXI.Mesh) {
|
||||||
|
this._pixi.shader.uniforms.uFreq = sf;
|
||||||
|
} else if (this._pixi instanceof PIXI.TilingSprite) {
|
||||||
|
// tileScale units are pixels, so converting function frequency to pixels
|
||||||
|
// and also taking into account possible size difference between used texture and requested stim size
|
||||||
|
this._pixi.tileScale.x = (1 / sf) * (this._pixi.width / this._pixi.texture.width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the stimulus, if necessary.
|
* Update the stimulus, if necessary.
|
||||||
*
|
*
|
||||||
* @name module:visual.ImageStim#_updateIfNeeded
|
* @name module:visual.GratingStim#_updateIfNeeded
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_updateIfNeeded()
|
_updateIfNeeded()
|
||||||
@ -396,13 +420,18 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
|
|||||||
|
|
||||||
if (this._tex instanceof HTMLImageElement)
|
if (this._tex instanceof HTMLImageElement)
|
||||||
{
|
{
|
||||||
this._pixi = PIXI.Sprite.from(this._tex);
|
this._pixi = PIXI.TilingSprite.from(this._tex, {
|
||||||
|
width: this._sizeInPixels[0],
|
||||||
|
height: this._sizeInPixels[1]
|
||||||
|
});
|
||||||
|
this.setPhase(this._phase);
|
||||||
|
this.setSpatialFrequency(this._spatialFrequency);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this._pixi = this._getPixiMeshFromPredefinedShaders(this._tex, {
|
this._pixi = this._getPixiMeshFromPredefinedShaders(this._tex, {
|
||||||
uFreq: this.spatialFrequency,
|
uFreq: this._spatialFrequency,
|
||||||
uPhase: this.phase
|
uPhase: this._phase
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this._pixi.pivot.set(this._pixi.width * .5, this._pixi.width * .5);
|
this._pixi.pivot.set(this._pixi.width * .5, this._pixi.width * .5);
|
||||||
@ -421,8 +450,8 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
|
|||||||
// rendering mask to texture for further use.
|
// rendering mask to texture for further use.
|
||||||
const maskMesh = this._getPixiMeshFromPredefinedShaders(this._mask);
|
const maskMesh = this._getPixiMeshFromPredefinedShaders(this._mask);
|
||||||
const rt = PIXI.RenderTexture.create({
|
const rt = PIXI.RenderTexture.create({
|
||||||
width: 256,
|
width: this._sizeInPixels[0],
|
||||||
height: 256
|
height: this._sizeInPixels[1]
|
||||||
});
|
});
|
||||||
this.win._renderer.render(maskMesh, {
|
this.win._renderer.render(maskMesh, {
|
||||||
renderTexture: rt
|
renderTexture: rt
|
||||||
@ -448,9 +477,9 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
|
|||||||
|
|
||||||
// set the scale:
|
// set the scale:
|
||||||
const displaySize = this._getDisplaySize();
|
const displaySize = this._getDisplaySize();
|
||||||
const size_px = util.to_px(displaySize, this.units, this.win);
|
this._sizeInPixels = util.to_px(displaySize, this.units, this.win);
|
||||||
const scaleX = size_px[0] / this._pixi.width;
|
const scaleX = this._sizeInPixels[0] / this._pixi.width;
|
||||||
const scaleY = size_px[1] / this._pixi.height;
|
const scaleY = this._sizeInPixels[1] / this._pixi.height;
|
||||||
this._pixi.scale.x = this.flipHoriz ? -scaleX : scaleX;
|
this._pixi.scale.x = this.flipHoriz ? -scaleX : scaleX;
|
||||||
this._pixi.scale.y = this.flipVert ? scaleY : -scaleY;
|
this._pixi.scale.y = this.flipVert ? scaleY : -scaleY;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user