From 3ebd9c4943403f330b994013925ba637e9483475 Mon Sep 17 00:00:00 2001 From: lgtst Date: Mon, 21 Mar 2022 23:28:54 +0300 Subject: [PATCH] new documentation for GratingStim, variables and methods renamed to match psychoPy --- docs/visual_GratingStim.js.html | 205 +++++++++++++++--------- package.json | 1 + scripts/build.js.cjs | 26 ++- src/visual/GratingStim.js | 203 ++++++++++++++--------- src/visual/shaders/circleShader.frag | 13 +- src/visual/shaders/crossShader.frag | 14 +- src/visual/shaders/gaussShader.frag | 14 +- src/visual/shaders/radRampShader.frag | 15 +- src/visual/shaders/raisedCosShader.frag | 14 +- src/visual/shaders/sawShader.frag | 14 +- src/visual/shaders/sinShader.frag | 11 ++ src/visual/shaders/sinXsinShader.frag | 11 ++ src/visual/shaders/sqrShader.frag | 14 +- src/visual/shaders/sqrXsqrShader.frag | 11 ++ src/visual/shaders/triShader.frag | 14 +- 15 files changed, 403 insertions(+), 177 deletions(-) diff --git a/docs/visual_GratingStim.js.html b/docs/visual_GratingStim.js.html index e6804ff..80cfac3 100644 --- a/docs/visual_GratingStim.js.html +++ b/docs/visual_GratingStim.js.html @@ -29,9 +29,9 @@
/**
  * Grating Stimulus.
  *
- * @author Alain Pitiot
+ * @author Alain Pitiot, Nikita Agafonov
  * @version 2021.2.0
- * @copyright (c) 2017-2020 Ilixa Ltd. (http://ilixa.com) (c) 2020-2021 Open Science Tools Ltd. (https://opensciencetools.org)
+ * @copyright (c) 2017-2020 Ilixa Ltd. (http://ilixa.com) (c) 2020-2022 Open Science Tools Ltd. (https://opensciencetools.org)
  * @license Distributed under the terms of the MIT License
  */
 
@@ -54,7 +54,6 @@ import crossShader from "./shaders/crossShader.frag";
 import radRampShader from "./shaders/radRampShader.frag";
 import raisedCosShader from "./shaders/raisedCosShader.frag";
 
-
 /**
  * Grating Stimulus.
  *
@@ -65,51 +64,108 @@ import raisedCosShader from "./shaders/raisedCosShader.frag";
  * @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.image - the name of the image resource or the HTMLImageElement corresponding to the image
- * @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 | 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=1.0] - phase of the function used in grating stimulus
  * @param {Array.<number>} [options.pos= [0, 0]] - the position of the center of the stimulus
  * @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 (DEFAULT_STIM_SIZE_PX will be used if size is not specified)
  * @param {Color} [options.color= "white"] the background color
  * @param {number} [options.opacity= 1.0] - the opacity
  * @param {number} [options.contrast= 1.0] - the contrast
  * @param {number} [options.depth= 0] - the depth (i.e. the z order)
- * @param {number} [options.texRes= 128] - the resolution of the text
- * @param {boolean} [options.interpolate= false] - whether or not the image is interpolated
+ * @param {boolean} [options.interpolate= false] - whether or not the image is interpolated. NOT IMPLEMENTED YET.
+ * @param {String} [options.blendmode= 'avg'] - blend mode of the stimulus, determines how the stimulus is blended with the background. NOT IMPLEMENTED YET.
  * @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
  */
 
 export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
 {
-	// win,
-	// tex="sin",
-	// mask="none",
-	// units="",
-	// pos=(0.0, 0.0),
-	// size=None,
-	// sf=None,
-	// ori=0.0,
-	// phase=(0.0, 0.0),
-	// texRes=128,
-	// rgb=None,
-	// dkl=None,
-	// lms=None,
-	// color=(1.0, 1.0, 1.0),
-	// colorSpace='rgb',
-	// contrast=1.0,
-	// opacity=None,
-	// depth=0,
-	// rgbPedestal=(0.0, 0.0, 0.0),
-	// interpolate=False,
-	// blendmode='avg',
-	// name=None,
-	// autoLog=None,
-	// autoDraw=False,
-	// maskParams=None)
-
-	static #DEFINED_FUNCTIONS = {
+	/**
+	 * An object that keeps shaders source code and default uniform values for them.
+	 * Shader source code is later used for construction of shader programs to create respective visual stimuli.
+	 * @name module:visual.GratingStim.#SHADERS
+	 * @type {Object}
+	 * @property {Object} sin - Creates 2d sine wave image as if 1d sine graph was extended across Z axis and observed from above.
+	 * {@link https://en.wikipedia.org/wiki/Sine_wave}
+	 * @property {String} sin.shader - shader source code for the sine wave stimuli
+	 * @property {Object} sin.uniforms - default uniforms for sine wave shader
+	 * @property {float} sin.uniforms.uFreq=1.0 - frequency of sine wave.
+	 * @property {float} sin.uniforms.uPhase=0.0 - phase of sine wave.
+	 *
+	 * @property {Object} sqr - Creates 2d square wave image as if 1d square graph was extended across Z axis and observed from above.
+	 * {@link https://en.wikipedia.org/wiki/Square_wave}
+	 * @property {String} sqr.shader - shader source code for the square wave stimuli
+	 * @property {Object} sqr.uniforms - default uniforms for square wave shader
+	 * @property {float} sqr.uniforms.uFreq=1.0 - frequency of square wave.
+	 * @property {float} sqr.uniforms.uPhase=0.0 - phase of square wave.
+	 *
+	 * @property {Object} saw - Creates 2d sawtooth wave image as if 1d sawtooth graph was extended across Z axis and observed from above.
+	 * {@link https://en.wikipedia.org/wiki/Sawtooth_wave}
+	 * @property {String} saw.shader - shader source code for the sawtooth wave stimuli
+	 * @property {Object} saw.uniforms - default uniforms for sawtooth wave shader
+	 * @property {float} saw.uniforms.uFreq=1.0 - frequency of sawtooth wave.
+	 * @property {float} saw.uniforms.uPhase=0.0 - phase of sawtooth wave.
+	 *
+	 * @property {Object} tri - Creates 2d triangle wave image as if 1d triangle graph was extended across Z axis and observed from above.
+	 * {@link https://en.wikipedia.org/wiki/Triangle_wave}
+	 * @property {String} tri.shader - shader source code for the triangle wave stimuli
+	 * @property {Object} tri.uniforms - default uniforms for triangle wave shader
+	 * @property {float} tri.uniforms.uFreq=1.0 - frequency of triangle wave.
+	 * @property {float} tri.uniforms.uPhase=0.0 - phase of triangle wave.
+	 * @property {float} tri.uniforms.uPeriod=1.0 - period of triangle wave.
+	 *
+	 * @property {Object} sinXsin - Creates an image of two 2d sine waves multiplied with each other.
+	 * {@link https://en.wikipedia.org/wiki/Sine_wave}
+	 * @property {String} sinXsin.shader - shader source code for the two multiplied sine waves stimuli
+	 * @property {Object} sinXsin.uniforms - default uniforms for shader
+	 * @property {float} sinXsin.uniforms.uFreq=1.0 - frequency of sine wave (both of them).
+	 * @property {float} sinXsin.uniforms.uPhase=0.0 - phase of sine wave (both of them).
+	 *
+	 * @property {Object} sqrXsqr - Creates an image of two 2d square waves multiplied with each other.
+	 * {@link https://en.wikipedia.org/wiki/Square_wave}
+	 * @property {String} sqrXsqr.shader - shader source code for the two multiplied sine waves stimuli
+	 * @property {Object} sqrXsqr.uniforms - default uniforms for shader
+	 * @property {float} sqrXsqr.uniforms.uFreq=1.0 - frequency of sine wave (both of them).
+	 * @property {float} sqrXsqr.uniforms.uPhase=0.0 - phase of sine wave (both of them).
+	 *
+	 * @property {Object} circle - Creates a filled circle shape with sharp edges.
+     * @property {String} circle.shader - shader source code for filled circle.
+     * @property {Object} circle.uniforms - default uniforms for shader.
+     * @property {float} circle.uniforms.uRadius=1.0 - Radius of the circle. Ranges [0.0, 1.0], where 0.0 is circle so tiny it results in empty stim
+     * and 1.0 is circle that spans from edge to edge of the stim.
+     *
+     * @property {Object} gauss - Creates a 2d Gaussian image as if 1d Gaussian graph was rotated arount Y axis and observed from above.
+	 * {@link https://en.wikipedia.org/wiki/Gaussian_function}
+	 * @property {String} gauss.shader - shader source code for Gaussian shader
+	 * @property {Object} gauss.uniforms - default uniforms for shader
+	 * @property {float} gauss.uniforms.uA=1.0 - A constant for gaussian formula (see link).
+	 * @property {float} gauss.uniforms.uB=0.0 - B constant for gaussian formula (see link).
+	 * @property {float} gauss.uniforms.uC=0.16 - C constant for gaussian formula (see link).
+	 *
+	 * @property {Object} cross - Creates a filled cross shape with sharp edges.
+     * @property {String} cross.shader - shader source code for cross shader
+     * @property {Object} cross.uniforms - default uniforms for shader
+     * @property {float} cross.uniforms.uThickness=0.2 - Thickness of the cross. Ranges [0.0, 1.0], where 0.0 thickness makes a cross so thin it becomes
+     * invisible and results in an empty stim and 1.0 makes it so thick it fills the entire stim.
+     *
+     * @property {Object} radRamp - Creates 2d radial ramp image.
+	 * @property {String} radRamp.shader - shader source code for radial ramp shader
+	 * @property {Object} radRamp.uniforms - default uniforms for shader
+	 * @property {float} radRamp.uniforms.uSqueeze=1.0 - coefficient that helps to modify size of the ramp. Ranges [0.0, Infinity], where 0.0 results in ramp being so large
+	 * it fills the entire stim and Infinity makes it so tiny it's invisible.
+	 *
+	 * @property {Object} raisedCos - Creates 2d raised-cosine image as if 1d raised-cosine graph was rotated around Y axis and observed from above.
+	 * {@link https://en.wikipedia.org/wiki/Raised-cosine_filter}
+     * @property {String} raisedCos.shader - shader source code for raised-cosine shader
+     * @property {Object} raisedCos.uniforms - default uniforms for shader
+     * @property {float} raisedCos.uniforms.uBeta=0.25 - roll-off factor (see link).
+     * @property {float} raisedCos.uniforms.uPeriod=0.625 - reciprocal of the symbol-rate (see link).
+	 */
+	static #SHADERS = {
 		sin: {
 			shader: sinShader,
 			uniforms: {
@@ -142,7 +198,8 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
 		sinXsin: {
 			shader: sinXsinShader,
 			uniforms: {
-
+				uFreq: 1.0,
+				uPhase: 0.0
 			}
 		},
 		sqrXsqr: {
@@ -155,7 +212,7 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
 		circle: {
 			shader: circleShader,
 			uniforms: {
-
+				uRadius: 1.0
 			}
 		},
 		gauss: {
@@ -169,24 +226,30 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
 		cross: {
 			shader: crossShader,
 			uniforms: {
-				uThickness: 0.1
+				uThickness: 0.2
 			}
 		},
 		radRamp: {
 			shader: radRampShader,
 			uniforms: {
-
+				uSqueeze: 1.0
 			}
 		},
 		raisedCos: {
 			shader: raisedCosShader,
 			uniforms: {
 				uBeta: 0.25,
-				uPeriod: 1.0
+				uPeriod: 0.625
 			}
 		}
 	};
 
+	/**
+	 * Default size of the Grating Stimuli in pixels.
+	 * @name module:visual.GratingStim.#DEFAULT_STIM_SIZE_PX
+	 * @type {Array}
+	 * @default [256, 256]
+	 */
 	static #DEFAULT_STIM_SIZE_PX = [256, 256]; // in pixels
 
 	constructor({
@@ -196,20 +259,15 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
 		mask,
 		pos,
 		units,
-		spatialFrequency = 1.,
+		sf = 1.0,
 		ori,
 		phase,
 		size,
-		rgb,
-	    dkl,
-	    lms,
 		color,
 		colorSpace,
 		opacity,
 		contrast,
-		texRes,
 		depth,
-		rgbPedestal,
 		interpolate,
 		blendmode,
 		autoDraw,
@@ -228,14 +286,14 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
 			mask,
 		);
 		this._addAttribute(
-			"spatialFrequency",
-			spatialFrequency,
-			GratingStim.#DEFINED_FUNCTIONS[tex].uniforms.uFreq || 1.0
+			"SF",
+			sf,
+			GratingStim.#SHADERS[tex] ? GratingStim.#SHADERS[tex].uniforms.uFreq || 1.0 : 1.0
 		);
 		this._addAttribute(
 			"phase",
 			phase,
-			GratingStim.#DEFINED_FUNCTIONS[tex].uniforms.uPhase || 0.0
+			GratingStim.#SHADERS[tex] ? GratingStim.#SHADERS[tex].uniforms.uPhase || 0.0 : 0.0
 		);
 		this._addAttribute(
 			"color",
@@ -249,12 +307,6 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
 			1.0,
 			this._onChange(true, false),
 		);
-		this._addAttribute(
-			"texRes",
-			texRes,
-			128,
-			this._onChange(true, false),
-		);
 		this._addAttribute(
 			"interpolate",
 			interpolate,
@@ -277,11 +329,11 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
 	}
 
 	/**
-	 * Setter for the image attribute.
+	 * Setter for the tex attribute.
 	 *
-	 * @name module:visual.GratingStim#setImage
+	 * @name module:visual.GratingStim#setTex
 	 * @public
-	 * @param {HTMLImageElement | string} image - the name of the image resource or HTMLImageElement corresponding to the image
+	 * @param {HTMLImageElement | string} tex - the name of built in shader function or name of the image resource or HTMLImageElement corresponding to the image
 	 * @param {boolean} [log= false] - whether of not to log
 	 */
 	setTex(tex, log = false)
@@ -301,7 +353,7 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
 				this.psychoJS.logger.warn("setting the tex of GratingStim: " + this._name + " with argument: undefined.");
 				this.psychoJS.logger.debug("set the tex of GratingStim: " + this._name + " as: undefined");
 			}
-			else if (GratingStim.#DEFINED_FUNCTIONS[tex] !== undefined)
+			else if (GratingStim.#SHADERS[tex] !== undefined)
 			{
 				// 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);
@@ -363,7 +415,7 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
 				this.psychoJS.logger.warn("setting the mask of GratingStim: " + this._name + " with argument: undefined.");
 				this.psychoJS.logger.debug("set the mask of GratingStim: " + this._name + " as: undefined");
 			}
-			else if (GratingStim.#DEFINED_FUNCTIONS[mask] !== undefined)
+			else if (GratingStim.#SHADERS[mask] !== undefined)
 			{
 				// mask is a string and it is one of predefined functions available in shaders
 				this.psychoJS.logger.debug("the mask is one of predefined functions. Set the mask of GratingStim: " + this._name + " as: " + mask);
@@ -449,8 +501,8 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
 	 * 
 	 * @name module:visual.GratingStim#_getPixiMeshFromPredefinedShaders
 	 * @function
-	 * @private
-	 * @param {String} funcName - name of the shader function. Must be one of the DEFINED_FUNCTIONS
+	 * @protected
+	 * @param {String} funcName - name of the shader function. Must be one of the SHADERS
 	 * @param {Object} uniforms - a set of uniforms to supply to the shader. Mixed together with default uniform values.
 	 * @return {Pixi.Mesh} Pixi.Mesh object that represents shader and later added to the scene.
 	 */
@@ -473,8 +525,8 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
 		);
 		geometry.addIndex([0, 1, 2, 0, 2, 3]);
 		const vertexSrc = defaultQuadVert;
-	    const fragmentSrc = GratingStim.#DEFINED_FUNCTIONS[funcName].shader;
-	    const uniformsFinal = Object.assign({}, GratingStim.#DEFINED_FUNCTIONS[funcName].uniforms, uniforms);
+	    const fragmentSrc = GratingStim.#SHADERS[funcName].shader;
+	    const uniformsFinal = Object.assign({}, GratingStim.#SHADERS[funcName].uniforms, uniforms);
 		const shader = PIXI.Shader.from(vertexSrc, fragmentSrc, uniformsFinal);
 		return new PIXI.Mesh(geometry, shader);
 	}
@@ -499,19 +551,22 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
 	/**
 	 * Set spatial frequency value for the function.
 	 * 
-	 * @name module:visual.GratingStim#setPhase
+	 * @name module:visual.GratingStim#setSF
 	 * @public
 	 * @param {number} sf - spatial frequency value
-	 * @param {boolean} [log= false] - whether of not to log
+	 * @param {boolean} [log=false] - whether or not to log
 	 */ 
-	setSpatialFrequency (sf, log = false) {
-		this._setAttribute("spatialFrequency", sf, log);
+	setSF (sf, log = false) {
+		this._setAttribute("SF", 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);
+			// since most functions defined in SHADERS assume spatial frequency change along X axis
+			// we assume desired effect for image based stims to be the same so tileScale.y is not affected by spatialFrequency
+			this._pixi.tileScale.y = this._pixi.height / this._pixi.texture.height;
 		}
 	}
 
@@ -552,16 +607,16 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
 					height: this._size_px[1]
 				});
 				this.setPhase(this._phase);
-				this.setSpatialFrequency(this._spatialFrequency);
+				this.setSF(this._SF);
 			}
 			else
 			{
 				this._pixi = this._getPixiMeshFromPredefinedShaders(this._tex, {
-					uFreq: this._spatialFrequency,
+					uFreq: this._SF,
 					uPhase: this._phase
 				});
 			}
-			this._pixi.pivot.set(this._pixi.width * .5, this._pixi.width * .5);
+			this._pixi.pivot.set(this._pixi.width * 0.5, this._pixi.width * 0.5);
 
 			// add a mask if need be:
 			if (typeof this._mask !== "undefined")
@@ -569,6 +624,8 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
 				if (this._mask instanceof HTMLImageElement)
 				{
 					this._pixi.mask = PIXI.Sprite.from(this._mask);
+					this._pixi.mask.width = this._size_px[0];
+					this._pixi.mask.height = this._size_px[1];
 					this._pixi.addChild(this._pixi.mask);
 				}
 				else
@@ -635,7 +692,7 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin)
 
- Documentation generated by JSDoc 3.6.7 on Wed Mar 02 2022 20:43:58 GMT+0300 (Moscow Standard Time) + Documentation generated by JSDoc 3.6.7 on Mon Mar 21 2022 21:35:47 GMT+0300 (Moscow Standard Time)
diff --git a/package.json b/package.json index 8f9f2d8..f2233a6 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "start": "npm run build" }, "dependencies": { + "esbuild-plugin-glsl": "^1.0.5", "howler": "^2.2.1", "log4javascript": "github:Ritzlgrmft/log4javascript", "pako": "^1.0.10", diff --git a/scripts/build.js.cjs b/scripts/build.js.cjs index 825e0cd..d7d720b 100644 --- a/scripts/build.js.cjs +++ b/scripts/build.js.cjs @@ -1,9 +1,18 @@ -const { buildSync } = require("esbuild"); -const pkg = require("psychojs/package.json"); +const { buildSync, build } = require("esbuild"); +const { glsl } = require("esbuild-plugin-glsl"); +const pkg = require("../package.json"); const versionMaybe = process.env.npm_config_outver; const dirMaybe = process.env.npm_config_outdir; const [, , , dir = dirMaybe || "out", version = versionMaybe || pkg.version] = process.argv; +let shouldWatchDir = false; + +for (var i = 0; i < process.argv.length; i++) { + if (process.argv[i] === '-w') { + shouldWatchDir = true; + break; + } +} [ // The ESM bundle @@ -20,13 +29,19 @@ const [, , , dir = dirMaybe || "out", version = versionMaybe || pkg.version] = p }, ].forEach(function(options) { - buildSync({ ...this, ...options }); + build({ ...this, ...options }) + .then(()=> { + if (shouldWatchDir) { + console.log('watching...') + } + }); }, { // Shared options banner: { js: `/*! For license information please see psychojs-${version}.js.LEGAL.txt */`, }, bundle: true, + watch: shouldWatchDir, sourcemap: true, entryPoints: ["src/index.js"], minifySyntax: true, @@ -36,4 +51,9 @@ const [, , , dir = dirMaybe || "out", version = versionMaybe || pkg.version] = p "es2017", "node14", ], + plugins: [ + glsl({ + minify: true + }) + ] }); diff --git a/src/visual/GratingStim.js b/src/visual/GratingStim.js index c06df13..a226c10 100644 --- a/src/visual/GratingStim.js +++ b/src/visual/GratingStim.js @@ -1,9 +1,9 @@ /** * Grating Stimulus. * - * @author Alain Pitiot + * @author Alain Pitiot, Nikita Agafonov * @version 2021.2.0 - * @copyright (c) 2017-2020 Ilixa Ltd. (http://ilixa.com) (c) 2020-2021 Open Science Tools Ltd. (https://opensciencetools.org) + * @copyright (c) 2017-2020 Ilixa Ltd. (http://ilixa.com) (c) 2020-2022 Open Science Tools Ltd. (https://opensciencetools.org) * @license Distributed under the terms of the MIT License */ @@ -26,7 +26,6 @@ import crossShader from "./shaders/crossShader.frag"; import radRampShader from "./shaders/radRampShader.frag"; import raisedCosShader from "./shaders/raisedCosShader.frag"; - /** * Grating Stimulus. * @@ -37,51 +36,108 @@ import raisedCosShader from "./shaders/raisedCosShader.frag"; * @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.image - the name of the image resource or the HTMLImageElement corresponding to the image - * @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 | 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=1.0] - phase of the function used in grating stimulus * @param {Array.} [options.pos= [0, 0]] - the position of the center of the stimulus * @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 (DEFAULT_STIM_SIZE_PX will be used if size is not specified) * @param {Color} [options.color= "white"] the background color * @param {number} [options.opacity= 1.0] - the opacity * @param {number} [options.contrast= 1.0] - the contrast * @param {number} [options.depth= 0] - the depth (i.e. the z order) - * @param {number} [options.texRes= 128] - the resolution of the text - * @param {boolean} [options.interpolate= false] - whether or not the image is interpolated + * @param {boolean} [options.interpolate= false] - whether or not the image is interpolated. NOT IMPLEMENTED YET. + * @param {String} [options.blendmode= 'avg'] - blend mode of the stimulus, determines how the stimulus is blended with the background. NOT IMPLEMENTED YET. * @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 */ export class GratingStim extends util.mix(VisualStim).with(ColorMixin) { - // win, - // tex="sin", - // mask="none", - // units="", - // pos=(0.0, 0.0), - // size=None, - // sf=None, - // ori=0.0, - // phase=(0.0, 0.0), - // texRes=128, - // rgb=None, - // dkl=None, - // lms=None, - // color=(1.0, 1.0, 1.0), - // colorSpace='rgb', - // contrast=1.0, - // opacity=None, - // depth=0, - // rgbPedestal=(0.0, 0.0, 0.0), - // interpolate=False, - // blendmode='avg', - // name=None, - // autoLog=None, - // autoDraw=False, - // maskParams=None) - - static #DEFINED_FUNCTIONS = { + /** + * An object that keeps shaders source code and default uniform values for them. + * Shader source code is later used for construction of shader programs to create respective visual stimuli. + * @name module:visual.GratingStim.#SHADERS + * @type {Object} + * @property {Object} sin - Creates 2d sine wave image as if 1d sine graph was extended across Z axis and observed from above. + * {@link https://en.wikipedia.org/wiki/Sine_wave} + * @property {String} sin.shader - shader source code for the sine wave stimuli + * @property {Object} sin.uniforms - default uniforms for sine wave shader + * @property {float} sin.uniforms.uFreq=1.0 - frequency of sine wave. + * @property {float} sin.uniforms.uPhase=0.0 - phase of sine wave. + * + * @property {Object} sqr - Creates 2d square wave image as if 1d square graph was extended across Z axis and observed from above. + * {@link https://en.wikipedia.org/wiki/Square_wave} + * @property {String} sqr.shader - shader source code for the square wave stimuli + * @property {Object} sqr.uniforms - default uniforms for square wave shader + * @property {float} sqr.uniforms.uFreq=1.0 - frequency of square wave. + * @property {float} sqr.uniforms.uPhase=0.0 - phase of square wave. + * + * @property {Object} saw - Creates 2d sawtooth wave image as if 1d sawtooth graph was extended across Z axis and observed from above. + * {@link https://en.wikipedia.org/wiki/Sawtooth_wave} + * @property {String} saw.shader - shader source code for the sawtooth wave stimuli + * @property {Object} saw.uniforms - default uniforms for sawtooth wave shader + * @property {float} saw.uniforms.uFreq=1.0 - frequency of sawtooth wave. + * @property {float} saw.uniforms.uPhase=0.0 - phase of sawtooth wave. + * + * @property {Object} tri - Creates 2d triangle wave image as if 1d triangle graph was extended across Z axis and observed from above. + * {@link https://en.wikipedia.org/wiki/Triangle_wave} + * @property {String} tri.shader - shader source code for the triangle wave stimuli + * @property {Object} tri.uniforms - default uniforms for triangle wave shader + * @property {float} tri.uniforms.uFreq=1.0 - frequency of triangle wave. + * @property {float} tri.uniforms.uPhase=0.0 - phase of triangle wave. + * @property {float} tri.uniforms.uPeriod=1.0 - period of triangle wave. + * + * @property {Object} sinXsin - Creates an image of two 2d sine waves multiplied with each other. + * {@link https://en.wikipedia.org/wiki/Sine_wave} + * @property {String} sinXsin.shader - shader source code for the two multiplied sine waves stimuli + * @property {Object} sinXsin.uniforms - default uniforms for shader + * @property {float} sinXsin.uniforms.uFreq=1.0 - frequency of sine wave (both of them). + * @property {float} sinXsin.uniforms.uPhase=0.0 - phase of sine wave (both of them). + * + * @property {Object} sqrXsqr - Creates an image of two 2d square waves multiplied with each other. + * {@link https://en.wikipedia.org/wiki/Square_wave} + * @property {String} sqrXsqr.shader - shader source code for the two multiplied sine waves stimuli + * @property {Object} sqrXsqr.uniforms - default uniforms for shader + * @property {float} sqrXsqr.uniforms.uFreq=1.0 - frequency of sine wave (both of them). + * @property {float} sqrXsqr.uniforms.uPhase=0.0 - phase of sine wave (both of them). + * + * @property {Object} circle - Creates a filled circle shape with sharp edges. + * @property {String} circle.shader - shader source code for filled circle. + * @property {Object} circle.uniforms - default uniforms for shader. + * @property {float} circle.uniforms.uRadius=1.0 - Radius of the circle. Ranges [0.0, 1.0], where 0.0 is circle so tiny it results in empty stim + * and 1.0 is circle that spans from edge to edge of the stim. + * + * @property {Object} gauss - Creates a 2d Gaussian image as if 1d Gaussian graph was rotated arount Y axis and observed from above. + * {@link https://en.wikipedia.org/wiki/Gaussian_function} + * @property {String} gauss.shader - shader source code for Gaussian shader + * @property {Object} gauss.uniforms - default uniforms for shader + * @property {float} gauss.uniforms.uA=1.0 - A constant for gaussian formula (see link). + * @property {float} gauss.uniforms.uB=0.0 - B constant for gaussian formula (see link). + * @property {float} gauss.uniforms.uC=0.16 - C constant for gaussian formula (see link). + * + * @property {Object} cross - Creates a filled cross shape with sharp edges. + * @property {String} cross.shader - shader source code for cross shader + * @property {Object} cross.uniforms - default uniforms for shader + * @property {float} cross.uniforms.uThickness=0.2 - Thickness of the cross. Ranges [0.0, 1.0], where 0.0 thickness makes a cross so thin it becomes + * invisible and results in an empty stim and 1.0 makes it so thick it fills the entire stim. + * + * @property {Object} radRamp - Creates 2d radial ramp image. + * @property {String} radRamp.shader - shader source code for radial ramp shader + * @property {Object} radRamp.uniforms - default uniforms for shader + * @property {float} radRamp.uniforms.uSqueeze=1.0 - coefficient that helps to modify size of the ramp. Ranges [0.0, Infinity], where 0.0 results in ramp being so large + * it fills the entire stim and Infinity makes it so tiny it's invisible. + * + * @property {Object} raisedCos - Creates 2d raised-cosine image as if 1d raised-cosine graph was rotated around Y axis and observed from above. + * {@link https://en.wikipedia.org/wiki/Raised-cosine_filter} + * @property {String} raisedCos.shader - shader source code for raised-cosine shader + * @property {Object} raisedCos.uniforms - default uniforms for shader + * @property {float} raisedCos.uniforms.uBeta=0.25 - roll-off factor (see link). + * @property {float} raisedCos.uniforms.uPeriod=0.625 - reciprocal of the symbol-rate (see link). + */ + static #SHADERS = { sin: { shader: sinShader, uniforms: { @@ -114,7 +170,8 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin) sinXsin: { shader: sinXsinShader, uniforms: { - + uFreq: 1.0, + uPhase: 0.0 } }, sqrXsqr: { @@ -127,7 +184,7 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin) circle: { shader: circleShader, uniforms: { - + uRadius: 1.0 } }, gauss: { @@ -141,24 +198,30 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin) cross: { shader: crossShader, uniforms: { - uThickness: 0.1 + uThickness: 0.2 } }, radRamp: { shader: radRampShader, uniforms: { - + uSqueeze: 1.0 } }, raisedCos: { shader: raisedCosShader, uniforms: { uBeta: 0.25, - uPeriod: 1.0 + uPeriod: 0.625 } } }; + /** + * Default size of the Grating Stimuli in pixels. + * @name module:visual.GratingStim.#DEFAULT_STIM_SIZE_PX + * @type {Array} + * @default [256, 256] + */ static #DEFAULT_STIM_SIZE_PX = [256, 256]; // in pixels constructor({ @@ -168,20 +231,15 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin) mask, pos, units, - spatialFrequency = 1., + sf = 1.0, ori, phase, size, - rgb, - dkl, - lms, color, colorSpace, opacity, contrast, - texRes, depth, - rgbPedestal, interpolate, blendmode, autoDraw, @@ -200,14 +258,14 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin) mask, ); this._addAttribute( - "spatialFrequency", - spatialFrequency, - GratingStim.#DEFINED_FUNCTIONS[tex].uniforms.uFreq || 1.0 + "SF", + sf, + GratingStim.#SHADERS[tex] ? GratingStim.#SHADERS[tex].uniforms.uFreq || 1.0 : 1.0 ); this._addAttribute( "phase", phase, - GratingStim.#DEFINED_FUNCTIONS[tex].uniforms.uPhase || 0.0 + GratingStim.#SHADERS[tex] ? GratingStim.#SHADERS[tex].uniforms.uPhase || 0.0 : 0.0 ); this._addAttribute( "color", @@ -221,12 +279,6 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin) 1.0, this._onChange(true, false), ); - this._addAttribute( - "texRes", - texRes, - 128, - this._onChange(true, false), - ); this._addAttribute( "interpolate", interpolate, @@ -249,11 +301,11 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin) } /** - * Setter for the image attribute. + * Setter for the tex attribute. * - * @name module:visual.GratingStim#setImage + * @name module:visual.GratingStim#setTex * @public - * @param {HTMLImageElement | string} image - the name of the image resource or HTMLImageElement corresponding to the image + * @param {HTMLImageElement | string} tex - the name of built in shader function or name of the image resource or HTMLImageElement corresponding to the image * @param {boolean} [log= false] - whether of not to log */ setTex(tex, log = false) @@ -273,7 +325,7 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin) this.psychoJS.logger.warn("setting the tex of GratingStim: " + this._name + " with argument: undefined."); this.psychoJS.logger.debug("set the tex of GratingStim: " + this._name + " as: undefined"); } - else if (GratingStim.#DEFINED_FUNCTIONS[tex] !== undefined) + else if (GratingStim.#SHADERS[tex] !== undefined) { // 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); @@ -335,7 +387,7 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin) this.psychoJS.logger.warn("setting the mask of GratingStim: " + this._name + " with argument: undefined."); this.psychoJS.logger.debug("set the mask of GratingStim: " + this._name + " as: undefined"); } - else if (GratingStim.#DEFINED_FUNCTIONS[mask] !== undefined) + else if (GratingStim.#SHADERS[mask] !== undefined) { // mask is a string and it is one of predefined functions available in shaders this.psychoJS.logger.debug("the mask is one of predefined functions. Set the mask of GratingStim: " + this._name + " as: " + mask); @@ -421,8 +473,8 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin) * * @name module:visual.GratingStim#_getPixiMeshFromPredefinedShaders * @function - * @private - * @param {String} funcName - name of the shader function. Must be one of the DEFINED_FUNCTIONS + * @protected + * @param {String} funcName - name of the shader function. Must be one of the SHADERS * @param {Object} uniforms - a set of uniforms to supply to the shader. Mixed together with default uniform values. * @return {Pixi.Mesh} Pixi.Mesh object that represents shader and later added to the scene. */ @@ -445,8 +497,8 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin) ); geometry.addIndex([0, 1, 2, 0, 2, 3]); const vertexSrc = defaultQuadVert; - const fragmentSrc = GratingStim.#DEFINED_FUNCTIONS[funcName].shader; - const uniformsFinal = Object.assign({}, GratingStim.#DEFINED_FUNCTIONS[funcName].uniforms, uniforms); + const fragmentSrc = GratingStim.#SHADERS[funcName].shader; + const uniformsFinal = Object.assign({}, GratingStim.#SHADERS[funcName].uniforms, uniforms); const shader = PIXI.Shader.from(vertexSrc, fragmentSrc, uniformsFinal); return new PIXI.Mesh(geometry, shader); } @@ -471,19 +523,22 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin) /** * Set spatial frequency value for the function. * - * @name module:visual.GratingStim#setPhase + * @name module:visual.GratingStim#setSF * @public * @param {number} sf - spatial frequency value - * @param {boolean} [log= false] - whether of not to log + * @param {boolean} [log=false] - whether or not to log */ - setSpatialFrequency (sf, log = false) { - this._setAttribute("spatialFrequency", sf, log); + setSF (sf, log = false) { + this._setAttribute("SF", 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); + // since most functions defined in SHADERS assume spatial frequency change along X axis + // we assume desired effect for image based stims to be the same so tileScale.y is not affected by spatialFrequency + this._pixi.tileScale.y = this._pixi.height / this._pixi.texture.height; } } @@ -524,16 +579,16 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin) height: this._size_px[1] }); this.setPhase(this._phase); - this.setSpatialFrequency(this._spatialFrequency); + this.setSF(this._SF); } else { this._pixi = this._getPixiMeshFromPredefinedShaders(this._tex, { - uFreq: this._spatialFrequency, + uFreq: this._SF, uPhase: this._phase }); } - this._pixi.pivot.set(this._pixi.width * .5, this._pixi.width * .5); + this._pixi.pivot.set(this._pixi.width * 0.5, this._pixi.width * 0.5); // add a mask if need be: if (typeof this._mask !== "undefined") @@ -541,6 +596,8 @@ export class GratingStim extends util.mix(VisualStim).with(ColorMixin) if (this._mask instanceof HTMLImageElement) { this._pixi.mask = PIXI.Sprite.from(this._mask); + this._pixi.mask.width = this._size_px[0]; + this._pixi.mask.height = this._size_px[1]; this._pixi.addChild(this._pixi.mask); } else diff --git a/src/visual/shaders/circleShader.frag b/src/visual/shaders/circleShader.frag index 3211825..51e9ecc 100644 --- a/src/visual/shaders/circleShader.frag +++ b/src/visual/shaders/circleShader.frag @@ -1,3 +1,13 @@ +/** + * Circle Shape. + * + * @author Nikita Agafonov + * @copyright (c) 2020-2022 Open Science Tools Ltd. (https://opensciencetools.org) + * @license Distributed under the terms of the MIT License + * @description Creates a filled circle shape with sharp edges. + * @usedby GratingStim.js + */ + #version 300 es precision mediump float; @@ -5,9 +15,10 @@ in vec2 vUvs; out vec4 shaderOut; #define M_PI 3.14159265358979 +uniform float uRadius; void main() { vec2 uv = vUvs; - float s = 1. - step(.5, length(uv - .5)); + float s = 1. - step(uRadius, length(uv * 2. - 1.)); shaderOut = vec4(vec3(s), 1.0); } diff --git a/src/visual/shaders/crossShader.frag b/src/visual/shaders/crossShader.frag index dbff4c1..b487b9e 100644 --- a/src/visual/shaders/crossShader.frag +++ b/src/visual/shaders/crossShader.frag @@ -1,3 +1,13 @@ +/** + * Cross Shape. + * + * @author Nikita Agafonov + * @copyright (c) 2020-2022 Open Science Tools Ltd. (https://opensciencetools.org) + * @license Distributed under the terms of the MIT License + * @description Creates a filled cross shape with sharp edges. + * @usedby GratingStim.js + */ + #version 300 es precision mediump float; @@ -9,8 +19,8 @@ uniform float uThickness; void main() { vec2 uv = vUvs; - float sx = step(uThickness, length(uv.x - .5)); - float sy = step(uThickness, length(uv.y - .5)); + float sx = step(uThickness, length(uv.x * 2. - 1.)); + float sy = step(uThickness, length(uv.y * 2. - 1.)); float s = 1. - sx * sy; shaderOut = vec4(vec3(s), 1.0); } diff --git a/src/visual/shaders/gaussShader.frag b/src/visual/shaders/gaussShader.frag index ed7fbec..3ba302c 100644 --- a/src/visual/shaders/gaussShader.frag +++ b/src/visual/shaders/gaussShader.frag @@ -1,7 +1,13 @@ -// -// Gaussian Function: -// https://en.wikipedia.org/wiki/Gaussian_function -// +/** + * Gaussian Function. + * https://en.wikipedia.org/wiki/Gaussian_function + * + * @author Nikita Agafonov + * @copyright (c) 2020-2022 Open Science Tools Ltd. (https://opensciencetools.org) + * @license Distributed under the terms of the MIT License + * @description Creates a 2d Gaussian image as if 1d Gaussian graph was rotated arount Y axis and observed from above. + * @usedby GratingStim.js + */ #version 300 es precision mediump float; diff --git a/src/visual/shaders/radRampShader.frag b/src/visual/shaders/radRampShader.frag index bbbc586..192acd4 100644 --- a/src/visual/shaders/radRampShader.frag +++ b/src/visual/shaders/radRampShader.frag @@ -1,17 +1,24 @@ -// -// Radial ramp function -// +/** + * Radial Ramp. + * + * @author Nikita Agafonov + * @copyright (c) 2020-2022 Open Science Tools Ltd. (https://opensciencetools.org) + * @license Distributed under the terms of the MIT License + * @description Creates 2d radial ramp image. + * @usedby GratingStim.js + */ #version 300 es precision mediump float; in vec2 vUvs; out vec4 shaderOut; +uniform float uSqueeze; #define M_PI 3.14159265358979 void main() { vec2 uv = vUvs; - float s = 1. - length(uv * 2. - 1.); + float s = 1. - length(uv * 2. - 1.) * uSqueeze; shaderOut = vec4(vec3(s), 1.0); } diff --git a/src/visual/shaders/raisedCosShader.frag b/src/visual/shaders/raisedCosShader.frag index 605fbfe..05e75cd 100644 --- a/src/visual/shaders/raisedCosShader.frag +++ b/src/visual/shaders/raisedCosShader.frag @@ -1,7 +1,13 @@ -// -// Raised-cosine function: -// https://en.wikipedia.org/wiki/Raised-cosine_filter -// +/** + * Raised-cosine. + * https://en.wikipedia.org/wiki/Raised-cosine_filter + * + * @author Nikita Agafonov + * @copyright (c) 2020-2022 Open Science Tools Ltd. (https://opensciencetools.org) + * @license Distributed under the terms of the MIT License + * @description Creates 2d raised-cosine image as if 1d raised-cosine graph was rotated around Y axis and observed from above. + * @usedby GratingStim.js + */ #version 300 es precision mediump float; diff --git a/src/visual/shaders/sawShader.frag b/src/visual/shaders/sawShader.frag index ed55ceb..0948bf7 100644 --- a/src/visual/shaders/sawShader.frag +++ b/src/visual/shaders/sawShader.frag @@ -1,7 +1,13 @@ -// -// Sawtooth wave: -// https://en.wikipedia.org/wiki/Sawtooth_wave -// +/** + * Sawtooth wave. + * https://en.wikipedia.org/wiki/Sawtooth_wave + * + * @author Nikita Agafonov + * @copyright (c) 2020-2022 Open Science Tools Ltd. (https://opensciencetools.org) + * @license Distributed under the terms of the MIT License + * @description Creates 2d sawtooth wave image as if 1d sawtooth graph was extended across Z axis and observed from above. + * @usedby GratingStim.js + */ #version 300 es precision mediump float; diff --git a/src/visual/shaders/sinShader.frag b/src/visual/shaders/sinShader.frag index abdd529..5e53d87 100644 --- a/src/visual/shaders/sinShader.frag +++ b/src/visual/shaders/sinShader.frag @@ -1,3 +1,14 @@ +/** + * Sine wave. + * https://en.wikipedia.org/wiki/Sine_wave + * + * @author Nikita Agafonov + * @copyright (c) 2020-2022 Open Science Tools Ltd. (https://opensciencetools.org) + * @license Distributed under the terms of the MIT License + * @description Creates 2d sine wave image as if 1d sine graph was extended across Z axis and observed from above. + * @usedby GratingStim.js + */ + #version 300 es precision mediump float; diff --git a/src/visual/shaders/sinXsinShader.frag b/src/visual/shaders/sinXsinShader.frag index 15435ac..9b8ffda 100644 --- a/src/visual/shaders/sinXsinShader.frag +++ b/src/visual/shaders/sinXsinShader.frag @@ -1,3 +1,14 @@ +/** + * Sine wave multiplied by another sine wave. + * https://en.wikipedia.org/wiki/Sine_wave + * + * @author Nikita Agafonov + * @copyright (c) 2020-2022 Open Science Tools Ltd. (https://opensciencetools.org) + * @license Distributed under the terms of the MIT License + * @description Creates an image of two 2d sine waves multiplied with each other. + * @usedby GratingStim.js + */ + #version 300 es precision mediump float; diff --git a/src/visual/shaders/sqrShader.frag b/src/visual/shaders/sqrShader.frag index dc9bd74..1d69602 100644 --- a/src/visual/shaders/sqrShader.frag +++ b/src/visual/shaders/sqrShader.frag @@ -1,7 +1,13 @@ -// -// Square wave: -// https://en.wikipedia.org/wiki/Square_wave -// +/** + * Square wave. + * https://en.wikipedia.org/wiki/Square_wave + * + * @author Nikita Agafonov + * @copyright (c) 2020-2022 Open Science Tools Ltd. (https://opensciencetools.org) + * @license Distributed under the terms of the MIT License + * @description Creates 2d square wave image as if 1d square graph was extended across Z axis and observed from above. + * @usedby GratingStim.js + */ #version 300 es precision mediump float; diff --git a/src/visual/shaders/sqrXsqrShader.frag b/src/visual/shaders/sqrXsqrShader.frag index 4eb6cfa..4f320ab 100644 --- a/src/visual/shaders/sqrXsqrShader.frag +++ b/src/visual/shaders/sqrXsqrShader.frag @@ -1,3 +1,14 @@ +/** + * Square wave multiplied by another square wave. + * https://en.wikipedia.org/wiki/Square_wave + * + * @author Nikita Agafonov + * @copyright (c) 2020-2022 Open Science Tools Ltd. (https://opensciencetools.org) + * @license Distributed under the terms of the MIT License + * @description Creates an image of two 2d square waves multiplied with each other. + * @usedby GratingStim.js + */ + #version 300 es precision mediump float; diff --git a/src/visual/shaders/triShader.frag b/src/visual/shaders/triShader.frag index 5b45ce0..445a0c4 100644 --- a/src/visual/shaders/triShader.frag +++ b/src/visual/shaders/triShader.frag @@ -1,7 +1,13 @@ -// -// Triangle wave: -// https://en.wikipedia.org/wiki/Triangle_wave -// +/** + * Triangle wave. + * https://en.wikipedia.org/wiki/Triangle_wave + * + * @author Nikita Agafonov + * @copyright (c) 2020-2022 Open Science Tools Ltd. (https://opensciencetools.org) + * @license Distributed under the terms of the MIT License + * @description Creates 2d triangle wave image as if 1d triangle graph was extended across Z axis and observed from above. + * @usedby GratingStim.js + */ #version 300 es precision mediump float;