From 0251a238f0c3bd6710c372b355b3f4d62eec1531 Mon Sep 17 00:00:00 2001 From: Avinash Kumar Deepak Date: Tue, 28 Oct 2025 00:37:26 +0530 Subject: [PATCH 1/2] feat(webgl): add global property support for p5.strands --- src/strands/strands_api.js | 67 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/strands/strands_api.js b/src/strands/strands_api.js index f4bfabcea2..6b6a46cbac 100644 --- a/src/strands/strands_api.js +++ b/src/strands/strands_api.js @@ -168,6 +168,73 @@ 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.int1 }, + { 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: 'mouseButton', type: DataType.int1 }, + { 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 From 12a3f50459e80fe9e07841f3c17372a11a74c2de Mon Sep 17 00:00:00 2001 From: Avinash Kumar Deepak Date: Mon, 24 Nov 2025 14:43:14 +0530 Subject: [PATCH 2/2] fix: change frameCount to float1, remove mouseButton, add unit tests - Changed frameCount from int1 to float1 for JS compatibility - Removed mouseButton (object type in p5 2.x) - Added unit tests for all 14 global properties Addresses feedback from @davepagurek in #8211 --- src/strands/strands_api.js | 3 +- test/unit/webgl/strands.js | 143 +++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 test/unit/webgl/strands.js diff --git a/src/strands/strands_api.js b/src/strands/strands_api.js index 6b6a46cbac..cd444998ae 100644 --- a/src/strands/strands_api.js +++ b/src/strands/strands_api.js @@ -180,13 +180,12 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) { { name: 'pmouseY', type: DataType.float1 }, { name: 'winMouseX', type: DataType.float1 }, { name: 'winMouseY', type: DataType.float1 }, - { name: 'frameCount', type: DataType.int1 }, + { 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: 'mouseButton', type: DataType.int1 }, { name: 'mouseIsPressed', type: DataType.bool1 } ]; 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(); + }); +});