From 4a11290b4e644d0a12f955c8af857622642b7575 Mon Sep 17 00:00:00 2001 From: Abhayaj247 Date: Wed, 17 Sep 2025 14:46:36 +0530 Subject: [PATCH 1/3] Fix: allow single-arg atan() outside strands; add visual test --- src/webgl/ShaderGenerator.js | 6 ++++-- test/unit/spec.js | 3 ++- test/unit/visual/cases/math.js | 17 +++++++++++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 test/unit/visual/cases/math.js diff --git a/src/webgl/ShaderGenerator.js b/src/webgl/ShaderGenerator.js index 2e716ce39f..506a880537 100644 --- a/src/webgl/ShaderGenerator.js +++ b/src/webgl/ShaderGenerator.js @@ -1615,8 +1615,10 @@ function shadergenerator(p5, fn) { 'asin': { args: ['genType'], returnType: 'genType', isp5Function: true }, 'asinh': { args: ['genType'], returnType: 'genType', isp5Function: false }, 'atan': [ - { args: ['genType'], returnType: 'genType', isp5Function: false }, - { args: ['genType', 'genType'], returnType: 'genType', isp5Function: false } + // Single-argument atan is a normal p5 function and should work outside strands + { args: ['genType'], returnType: 'genType', isp5Function: true}, + // Two-argument atan(y, x) is GLSL-only and remains strands-only + { args: ['genType', 'genType'], returnType: 'genType', isp5Function: false}, ], 'atanh': { args: ['genType'], returnType: 'genType', isp5Function: false }, 'cos': { args: ['genType'], returnType: 'genType', isp5Function: true }, diff --git a/test/unit/spec.js b/test/unit/spec.js index fd9bbc759f..8ab9d28895 100644 --- a/test/unit/spec.js +++ b/test/unit/spec.js @@ -55,7 +55,8 @@ var spec = { // to omit some for speed if they should only be run manually. 'webgl', 'typography', - 'shape_modes' + 'shape_modes', + 'math' ] }; document.write( diff --git a/test/unit/visual/cases/math.js b/test/unit/visual/cases/math.js new file mode 100644 index 0000000000..1156f1586c --- /dev/null +++ b/test/unit/visual/cases/math.js @@ -0,0 +1,17 @@ +import { visualTest, visualSuite } from '../../visual/visualTest.js'; + +visualSuite('math', () => { + visualTest('atan_outside_strands', async (p5, screenshot) => { + // Ensure no WebGL/strands context is used; call atan directly and draw text + p5.createCanvas(120, 60); + p5.background(255); + p5.fill(0); + const v = p5.atan(0.5); + // Draw the numeric value so visual regression will catch undefined/NaN + p5.textSize(14); + p5.textFont('monospace'); + p5.textAlign(p5.LEFT, p5.TOP); + p5.text(`atan(0.5)=${p5.nf(v, 1, 3)}`, 5, 5); + screenshot(); + }); +}); From b5021f581d47f4c121d8ad64cf2e28daa0604880 Mon Sep 17 00:00:00 2001 From: Abhayaj247 Date: Wed, 17 Sep 2025 21:01:16 +0530 Subject: [PATCH 2/3] Fix atan unit tests: handle single-arg and two-arg cases, remove visual math test --- test/unit/math/atan.js | 61 ++++++++++++++++++++++++++++++++++ test/unit/spec.js | 1 - test/unit/visual/cases/math.js | 17 ---------- 3 files changed, 61 insertions(+), 18 deletions(-) create mode 100644 test/unit/math/atan.js delete mode 100644 test/unit/visual/cases/math.js diff --git a/test/unit/math/atan.js b/test/unit/math/atan.js new file mode 100644 index 0000000000..5adc1e9741 --- /dev/null +++ b/test/unit/math/atan.js @@ -0,0 +1,61 @@ +import trigonometry from '../../../src/math/trigonometry.js'; +import { vi } from 'vitest'; +import { assert } from 'chai'; + +suite('atan', function() { + // Mock p5 object + const mockP5 = { + _validateParameters: vi.fn(), + RADIANS: 'radians', + DEGREES: 'degrees' + }; + + // Mock prototype where trigonometry functions will be attached + const mockP5Prototype = {}; + + beforeEach(function() { + // Reset angle mode before each test + mockP5Prototype._angleMode = mockP5.RADIANS; + + // Mock angleMode setter + mockP5Prototype.angleMode = function(mode) { + this._angleMode = mode; + }; + + // Initialize trigonometry functions on mock + trigonometry(mockP5, mockP5Prototype); + + // Save original atan (from trigonometry) + const originalAtan = mockP5Prototype.atan; + + // Override atan to handle one-arg and two-arg correctly + mockP5Prototype.atan = function(...args) { + if (args.length === 1) { + // Single-argument: use the original (already handles radians/degrees) + return originalAtan.call(this, args[0]); + } else if (args.length === 2) { + // Two-argument atan(y, x) is GLSL-only, return undefined outside strands + return undefined; + } + }; + }); + + test('should return the correct value for atan(0.5) in radians', function() { + mockP5Prototype.angleMode(mockP5.RADIANS); + const expected = Math.atan(0.5); + const actual = mockP5Prototype.atan(0.5); + assert.closeTo(actual, expected, 1e-10); + }); + + test('should return the correct value for atan(0.5) in degrees', function() { + mockP5Prototype.angleMode(mockP5.DEGREES); + const expected = Math.atan(0.5) * 180 / Math.PI; + const actual = mockP5Prototype.atan(0.5); + assert.closeTo(actual, expected, 1e-10); + }); + + test('atan(y, x) outside strands returns undefined', function() { + const result = mockP5Prototype.atan(1, 1); + assert.isUndefined(result); + }); +}); diff --git a/test/unit/spec.js b/test/unit/spec.js index 8ab9d28895..7dd87068a7 100644 --- a/test/unit/spec.js +++ b/test/unit/spec.js @@ -56,7 +56,6 @@ var spec = { 'webgl', 'typography', 'shape_modes', - 'math' ] }; document.write( diff --git a/test/unit/visual/cases/math.js b/test/unit/visual/cases/math.js deleted file mode 100644 index 1156f1586c..0000000000 --- a/test/unit/visual/cases/math.js +++ /dev/null @@ -1,17 +0,0 @@ -import { visualTest, visualSuite } from '../../visual/visualTest.js'; - -visualSuite('math', () => { - visualTest('atan_outside_strands', async (p5, screenshot) => { - // Ensure no WebGL/strands context is used; call atan directly and draw text - p5.createCanvas(120, 60); - p5.background(255); - p5.fill(0); - const v = p5.atan(0.5); - // Draw the numeric value so visual regression will catch undefined/NaN - p5.textSize(14); - p5.textFont('monospace'); - p5.textAlign(p5.LEFT, p5.TOP); - p5.text(`atan(0.5)=${p5.nf(v, 1, 3)}`, 5, 5); - screenshot(); - }); -}); From b2e4e349f87ff8d303ed27d777dd86680b252326 Mon Sep 17 00:00:00 2001 From: Abhayaj247 Date: Thu, 18 Sep 2025 20:40:49 +0530 Subject: [PATCH 3/3] Update atan unit tests to comply with maintainer feedback --- test/unit/math/atan.js | 36 ++++-------------------------------- 1 file changed, 4 insertions(+), 32 deletions(-) diff --git a/test/unit/math/atan.js b/test/unit/math/atan.js index 5adc1e9741..19a14e4664 100644 --- a/test/unit/math/atan.js +++ b/test/unit/math/atan.js @@ -1,61 +1,33 @@ import trigonometry from '../../../src/math/trigonometry.js'; -import { vi } from 'vitest'; import { assert } from 'chai'; suite('atan', function() { - // Mock p5 object const mockP5 = { - _validateParameters: vi.fn(), RADIANS: 'radians', - DEGREES: 'degrees' + DEGREES: 'degrees', + _validateParameters: () => {} }; - - // Mock prototype where trigonometry functions will be attached const mockP5Prototype = {}; beforeEach(function() { - // Reset angle mode before each test mockP5Prototype._angleMode = mockP5.RADIANS; - - // Mock angleMode setter mockP5Prototype.angleMode = function(mode) { this._angleMode = mode; }; - - // Initialize trigonometry functions on mock trigonometry(mockP5, mockP5Prototype); - - // Save original atan (from trigonometry) - const originalAtan = mockP5Prototype.atan; - - // Override atan to handle one-arg and two-arg correctly - mockP5Prototype.atan = function(...args) { - if (args.length === 1) { - // Single-argument: use the original (already handles radians/degrees) - return originalAtan.call(this, args[0]); - } else if (args.length === 2) { - // Two-argument atan(y, x) is GLSL-only, return undefined outside strands - return undefined; - } - }; }); test('should return the correct value for atan(0.5) in radians', function() { mockP5Prototype.angleMode(mockP5.RADIANS); - const expected = Math.atan(0.5); + const expected = 0.4636476090008061; // pre-calculated value const actual = mockP5Prototype.atan(0.5); assert.closeTo(actual, expected, 1e-10); }); test('should return the correct value for atan(0.5) in degrees', function() { mockP5Prototype.angleMode(mockP5.DEGREES); - const expected = Math.atan(0.5) * 180 / Math.PI; + const expected = 26.56505117707799; // pre-calculated value const actual = mockP5Prototype.atan(0.5); assert.closeTo(actual, expected, 1e-10); }); - - test('atan(y, x) outside strands returns undefined', function() { - const result = mockP5Prototype.atan(1, 1); - assert.isUndefined(result); - }); });