Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/webgl/ShaderGenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down
61 changes: 61 additions & 0 deletions test/unit/math/atan.js
Original file line number Diff line number Diff line change
@@ -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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we test the actual atan without testing a mock version? I think the only thing we really need a test for is just that, outside of strands, you can call atan with one argument, which it already supports without needing an override.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason being, it becomes a bit unclear how much of the functionality we're testing is just in the mock once we start doing this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @davepagurek ,

I’ve updated the atan unit test to only verify single-argument behavior (in both radians and degrees) using the actual function, without overriding or mocking it.


import trigonometry from '../../../src/math/trigonometry.js';
import { assert } from 'chai';

suite('atan', function() {
  const mockP5 = {
    RADIANS: 'radians',
    DEGREES: 'degrees',
    _validateParameters: () => {}
  };
  const mockP5Prototype = {};

  beforeEach(function() {
    mockP5Prototype._angleMode = mockP5.RADIANS;
    mockP5Prototype.angleMode = function(mode) {
      this._angleMode = mode;
    };
    trigonometry(mockP5, mockP5Prototype);
  });

  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);
  });
});

Could you please confirm if this approach looks good before I commit and push the changes?

Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That approach looks right! maybe just evaluate the two functions on a calculator to get as specific of a value to compare to as possible for your radians and degrees cases.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clarification @davepagurek. I’ve updated the tests to directly compare against pre-calculated values for atan(0.5) in both radians and degrees. Here’s the full updated test file :


import trigonometry from '../../../src/math/trigonometry.js';
import { assert } from 'chai';

suite('atan', function() {
  const mockP5 = {
    RADIANS: 'radians',
    DEGREES: 'degrees',
    _validateParameters: () => {}
  };
  const mockP5Prototype = {};

  beforeEach(function() {
    mockP5Prototype._angleMode = mockP5.RADIANS;
    mockP5Prototype.angleMode = function(mode) {
      this._angleMode = mode;
    };
    trigonometry(mockP5, mockP5Prototype);
  });

  test('should return the correct value for atan(0.5) in radians', function() {
    mockP5Prototype.angleMode(mockP5.RADIANS);
    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 = 26.56505117707799; // pre-calculated value
    const actual = mockP5Prototype.atan(0.5);
    assert.closeTo(actual, expected, 1e-10);
  });
});

Could you confirm if this looks good before I push the changes? Happy to make any further adjustments if needed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for reviewing and confirming, @davepagurek! I’ve pushed the changes accordingly. Please let me know if there’s anything else needed.

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);
});
});
2 changes: 1 addition & 1 deletion test/unit/spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ var spec = {
// to omit some for speed if they should only be run manually.
'webgl',
'typography',
'shape_modes'
'shape_modes',
]
};
document.write(
Expand Down
Loading