From 2f7206c9d85d80a9c3caf29d049f697b133f3ec8 Mon Sep 17 00:00:00 2001 From: nityam Date: Tue, 28 Apr 2026 22:06:07 +0530 Subject: [PATCH 1/2] support map() inside p5.strands shaders --- src/strands/strands_api.js | 17 +++++++++++++++ test/unit/webgl/p5.Shader.js | 40 ++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/strands/strands_api.js b/src/strands/strands_api.js index df7e8e8481..0bf7cfcf8f 100644 --- a/src/strands/strands_api.js +++ b/src/strands/strands_api.js @@ -294,6 +294,23 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) { } }); + const originalMap = fn.map; + augmentFn(fn, p5, 'map', function (...args) { + if (!strandsContext.active) { + return originalMap.apply(this, args); + } + const [n, start1, stop1, start2, stop2, withinBounds] = args; + const nNode = p5.strandsNode(n); + const start1Node = p5.strandsNode(start1); + const stop1Node = p5.strandsNode(stop1); + const t = nNode.sub(start1Node).div(stop1Node.sub(start1Node)); + const result = fn.mix(start2, stop2, t); + if (withinBounds) { + return fn.clamp(result, fn.min(start2, stop2), fn.max(start2, stop2)); + } + return result; + }); + augmentFn(fn, p5, 'getTexture', function (...rawArgs) { if (strandsContext.active) { const { id, dimension } = strandsContext.backend.createGetTextureCall(strandsContext, rawArgs); diff --git a/test/unit/webgl/p5.Shader.js b/test/unit/webgl/p5.Shader.js index 1e307f51cc..527e946407 100644 --- a/test/unit/webgl/p5.Shader.js +++ b/test/unit/webgl/p5.Shader.js @@ -495,6 +495,46 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca }); + test('map() works inside a strands modify callback', () => { + myp5.createCanvas(50, 50, myp5.WEBGL); + const testShader = myp5.baseMaterialShader().modify(() => { + myp5.getPixelInputs(inputs => { + const v = myp5.map(0.25, 0, 1, 0, 1); + inputs.color = [v, v, v, 1.0]; + return inputs; + }); + }, { myp5 }); + + myp5.noStroke(); + myp5.shader(testShader); + myp5.plane(myp5.width, myp5.height); + + const pixelColor = myp5.get(25, 25); + assert.approximately(pixelColor[0], 64, 5); + assert.approximately(pixelColor[1], 64, 5); + assert.approximately(pixelColor[2], 64, 5); + }); + + test('map() with withinBounds clamps the result inside strands', () => { + myp5.createCanvas(50, 50, myp5.WEBGL); + const testShader = myp5.baseMaterialShader().modify(() => { + myp5.getPixelInputs(inputs => { + const v = myp5.map(2.0, 0, 1, 0, 1, true); + inputs.color = [v, v, v, 1.0]; + return inputs; + }); + }, { myp5 }); + + myp5.noStroke(); + myp5.shader(testShader); + myp5.plane(myp5.width, myp5.height); + + const pixelColor = myp5.get(25, 25); + assert.approximately(pixelColor[0], 255, 5); + assert.approximately(pixelColor[1], 255, 5); + assert.approximately(pixelColor[2], 255, 5); + }); + test('handle custom uniform names with automatic values', () => { myp5.createCanvas(50, 50, myp5.WEBGL); const testShader = myp5.baseMaterialShader().modify(() => { From e21a46b94e6e7e0bbf8bc6cc7ada5a74362b08cc Mon Sep 17 00:00:00 2001 From: nityam Date: Mon, 4 May 2026 15:42:26 +0530 Subject: [PATCH 2/2] address review: switch fn. to this. and add range/gradient tests --- src/strands/strands_api.js | 4 +- test/unit/webgl/p5.Shader.js | 82 ++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/src/strands/strands_api.js b/src/strands/strands_api.js index 0bf7cfcf8f..3c64aa48ab 100644 --- a/src/strands/strands_api.js +++ b/src/strands/strands_api.js @@ -304,9 +304,9 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) { const start1Node = p5.strandsNode(start1); const stop1Node = p5.strandsNode(stop1); const t = nNode.sub(start1Node).div(stop1Node.sub(start1Node)); - const result = fn.mix(start2, stop2, t); + const result = this.mix(start2, stop2, t); if (withinBounds) { - return fn.clamp(result, fn.min(start2, stop2), fn.max(start2, stop2)); + return this.clamp(result, this.min(start2, stop2), this.max(start2, stop2)); } return result; }); diff --git a/test/unit/webgl/p5.Shader.js b/test/unit/webgl/p5.Shader.js index 527e946407..dbf45f1f56 100644 --- a/test/unit/webgl/p5.Shader.js +++ b/test/unit/webgl/p5.Shader.js @@ -535,6 +535,88 @@ test('returns numbers for builtin globals outside hooks and a strandNode when ca assert.approximately(pixelColor[2], 255, 5); }); + test('map() shrinks a wider input range into a narrower output range', () => { + myp5.createCanvas(50, 50, myp5.WEBGL); + const testShader = myp5.baseMaterialShader().modify(() => { + myp5.getPixelInputs(inputs => { + const v = myp5.map(5, 0, 10, 0, 1); + inputs.color = [v, v, v, 1.0]; + return inputs; + }); + }, { myp5 }); + + myp5.noStroke(); + myp5.shader(testShader); + myp5.plane(myp5.width, myp5.height); + + const pixelColor = myp5.get(25, 25); + assert.approximately(pixelColor[0], 128, 5); + assert.approximately(pixelColor[1], 128, 5); + assert.approximately(pixelColor[2], 128, 5); + }); + + test('map() handles offset output ranges correctly', () => { + myp5.createCanvas(50, 50, myp5.WEBGL); + const testShader = myp5.baseMaterialShader().modify(() => { + myp5.getPixelInputs(inputs => { + const v = myp5.map(0.5, 0, 1, 0.2, 0.8); + inputs.color = [v, v, v, 1.0]; + return inputs; + }); + }, { myp5 }); + + myp5.noStroke(); + myp5.shader(testShader); + myp5.plane(myp5.width, myp5.height); + + const pixelColor = myp5.get(25, 25); + assert.approximately(pixelColor[0], 128, 5); + assert.approximately(pixelColor[1], 128, 5); + assert.approximately(pixelColor[2], 128, 5); + }); + + test('map() handles a negative input range', () => { + myp5.createCanvas(50, 50, myp5.WEBGL); + const testShader = myp5.baseMaterialShader().modify(() => { + myp5.getPixelInputs(inputs => { + const v = myp5.map(0, -1, 1, 0, 1); + inputs.color = [v, v, v, 1.0]; + return inputs; + }); + }, { myp5 }); + + myp5.noStroke(); + myp5.shader(testShader); + myp5.plane(myp5.width, myp5.height); + + const pixelColor = myp5.get(25, 25); + assert.approximately(pixelColor[0], 128, 5); + assert.approximately(pixelColor[1], 128, 5); + assert.approximately(pixelColor[2], 128, 5); + }); + + test('map() remaps texCoord.x into a horizontal gradient', () => { + myp5.createCanvas(50, 50, myp5.WEBGL); + const testShader = myp5.baseMaterialShader().modify(() => { + myp5.getPixelInputs(inputs => { + const v = myp5.map(inputs.texCoord.x, 0, 1, 0.2, 0.8); + inputs.color = [v, v, v, 1.0]; + return inputs; + }); + }, { myp5 }); + + myp5.noStroke(); + myp5.shader(testShader); + myp5.plane(myp5.width, myp5.height); + + const left = myp5.get(2, 25); + const middle = myp5.get(25, 25); + const right = myp5.get(47, 25); + assert.approximately(left[0], 51, 10); + assert.approximately(middle[0], 128, 10); + assert.approximately(right[0], 204, 10); + }); + test('handle custom uniform names with automatic values', () => { myp5.createCanvas(50, 50, myp5.WEBGL); const testShader = myp5.baseMaterialShader().modify(() => {