diff --git a/src/strands/strands_api.js b/src/strands/strands_api.js index f4bfabcea2..cd444998ae 100644 --- a/src/strands/strands_api.js +++ b/src/strands/strands_api.js @@ -168,6 +168,72 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) { } } } + ////////////////////////////////////////////// + // Global p5 properties + ////////////////////////////////////////////// + const globalProperties = [ + { name: 'width', type: DataType.float1 }, + { name: 'height', type: DataType.float1 }, + { name: 'mouseX', type: DataType.float1 }, + { name: 'mouseY', type: DataType.float1 }, + { name: 'pmouseX', type: DataType.float1 }, + { name: 'pmouseY', type: DataType.float1 }, + { name: 'winMouseX', type: DataType.float1 }, + { name: 'winMouseY', type: DataType.float1 }, + { name: 'frameCount', type: DataType.float1 }, + { name: 'focused', type: DataType.bool1 }, + { name: 'displayWidth', type: DataType.float1 }, + { name: 'displayHeight', type: DataType.float1 }, + { name: 'windowWidth', type: DataType.float1 }, + { name: 'windowHeight', type: DataType.float1 }, + { name: 'mouseIsPressed', type: DataType.bool1 } + ]; + + const originalDescriptors = {}; + for (const { name } of globalProperties) { + originalDescriptors[name] = Object.getOwnPropertyDescriptor(fn, name) || { + get: function() { return p5.prototype[name]; }, + configurable: true + }; + } + + for (const { name, type } of globalProperties) { + strandsContext.fnOverrides[name] = originalDescriptors[name]; + + (function(propName, propType, origDescriptor) { + Object.defineProperty(fn, propName, { + get: function() { + if (strandsContext.active) { + const uniformName = `_p5_${propName}`; + const existingUniform = strandsContext.uniforms.find(u => u.name === uniformName); + + if (!existingUniform) { + const { id, dimension } = build.variableNode(strandsContext, propType, uniformName); + strandsContext.uniforms.push({ + name: uniformName, + typeInfo: propType, + defaultValue: origDescriptor.get ? + () => origDescriptor.get.call(fn) : + () => origDescriptor.value + }); + return createStrandsNode(id, dimension, strandsContext); + } else { + const { id, dimension } = build.variableNode(strandsContext, propType, uniformName); + return createStrandsNode(id, dimension, strandsContext); + } + } else { + if (origDescriptor.get) { + return origDescriptor.get.call(this); + } else { + return origDescriptor.value; + } + } + }, + configurable: true, + enumerable: true + }); + })(name, type, originalDescriptors[name]); + } } ////////////////////////////////////////////// // Per-Hook functions diff --git a/test/unit/webgl/strands.js b/test/unit/webgl/strands.js new file mode 100644 index 0000000000..0cff454779 --- /dev/null +++ b/test/unit/webgl/strands.js @@ -0,0 +1,143 @@ +suite('p5.strands global properties', function() { + test('width and height are defined', function() { + const p = new p5((sketch) => { + sketch.setup = function() { + sketch.createCanvas(200, 150); + }; + sketch.draw = function() {}; + }); + + assert.equal(p.width, 200); + assert.equal(p.height, 150); + p.remove(); + }); + + test('mouseX and mouseY are numbers', function() { + const p = new p5((sketch) => { + sketch.setup = function() { + sketch.createCanvas(100, 100); + }; + sketch.draw = function() {}; + }); + + assert.isNumber(p.mouseX); + assert.isNumber(p.mouseY); + p.remove(); + }); + + test('frameCount is a number', function() { + const p = new p5((sketch) => { + sketch.setup = function() { + sketch.createCanvas(100, 100); + }; + sketch.draw = function() {}; + }); + + assert.isNumber(p.frameCount); + assert.isAtLeast(p.frameCount, 0); + p.remove(); + }); + + test('pmouseX and pmouseY are numbers', function() { + const p = new p5((sketch) => { + sketch.setup = function() { + sketch.createCanvas(100, 100); + }; + sketch.draw = function() {}; + }); + + assert.isNumber(p.pmouseX); + assert.isNumber(p.pmouseY); + p.remove(); + }); + + test('focused is a boolean', function() { + const p = new p5((sketch) => { + sketch.setup = function() { + sketch.createCanvas(100, 100); + }; + sketch.draw = function() {}; + }); + + assert.isBoolean(p.focused); + p.remove(); + }); + + test('displayWidth and displayHeight are numbers', function() { + const p = new p5((sketch) => { + sketch.setup = function() { + sketch.createCanvas(100, 100); + }; + sketch.draw = function() {}; + }); + + assert.isNumber(p.displayWidth); + assert.isNumber(p.displayHeight); + p.remove(); + }); + + test('windowWidth and windowHeight are numbers', function() { + const p = new p5((sketch) => { + sketch.setup = function() { + sketch.createCanvas(100, 100); + }; + sketch.draw = function() {}; + }); + + assert.isNumber(p.windowWidth); + assert.isNumber(p.windowHeight); + p.remove(); + }); + + test('winMouseX and winMouseY are numbers', function() { + const p = new p5((sketch) => { + sketch.setup = function() { + sketch.createCanvas(100, 100); + }; + sketch.draw = function() {}; + }); + + assert.isNumber(p.winMouseX); + assert.isNumber(p.winMouseY); + p.remove(); + }); + + test('mouseIsPressed is a boolean', function() { + const p = new p5((sketch) => { + sketch.setup = function() { + sketch.createCanvas(100, 100); + }; + sketch.draw = function() {}; + }); + + assert.isBoolean(p.mouseIsPressed); + p.remove(); + }); + + test('all global properties are accessible', function() { + const p = new p5((sketch) => { + sketch.setup = function() { + sketch.createCanvas(100, 100); + }; + sketch.draw = function() {}; + }); + + assert.isDefined(p.width); + assert.isDefined(p.height); + assert.isDefined(p.mouseX); + assert.isDefined(p.mouseY); + assert.isDefined(p.pmouseX); + assert.isDefined(p.pmouseY); + assert.isDefined(p.winMouseX); + assert.isDefined(p.winMouseY); + assert.isDefined(p.frameCount); + assert.isDefined(p.focused); + assert.isDefined(p.displayWidth); + assert.isDefined(p.displayHeight); + assert.isDefined(p.windowWidth); + assert.isDefined(p.windowHeight); + assert.isDefined(p.mouseIsPressed); + + p.remove(); + }); +});