Skip to content

Commit

Permalink
progress on dot Util separation, see #4
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathanolson committed Feb 13, 2025
1 parent d1f7ba7 commit 10dd112
Show file tree
Hide file tree
Showing 16 changed files with 189 additions and 67 deletions.
7 changes: 4 additions & 3 deletions js/LinearFunction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import dot from './dot.js';
import Utils from './Utils.js';
import { clamp } from './util/clamp.js';

export default class LinearFunction {
private a1: number;
Expand Down Expand Up @@ -58,12 +59,12 @@ export default class LinearFunction {
* f( a1 ) = b1, f( a2 ) = b2, f( a3 ) = <linear mapped value>
* Optionally clamp the result to the range [b1,b2].
*/
const map = ( a1: number, a2: number, b1: number, b2: number, a3: number, clamp: boolean ): number => {
const map = ( a1: number, a2: number, b1: number, b2: number, a3: number, clampValue: boolean ): number => {
let b3 = Utils.linear( a1, a2, b1, b2, a3 );
if ( clamp ) {
if ( clampValue ) {
const max = Math.max( b1, b2 );
const min = Math.min( b1, b2 );
b3 = Utils.clamp( b3, min, max );
b3 = clamp( b3, min, max );
}
return b3;
};
Expand Down
59 changes: 26 additions & 33 deletions js/Utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@

import Big from '../../sherpa/lib/big-6.2.1.js'; // eslint-disable-line phet/default-import-match-filename
import dot from './dot.js';
import clamp from './util/clamp.js';
import roundSymmetric from './util/roundSymmetric.js';
import { clamp } from './util/clamp.js';
import { moduloBetweenDown } from './util/moduloBetweenDown.js';
import { moduloBetweenUp } from './util/moduloBetweenUp.js';
import { rangeExclusive } from './util/rangeExclusive.js';
import { rangeInclusive } from './util/rangeInclusive.js';
import { roundSymmetric } from './util/roundSymmetric.js';
import { toDegrees } from './util/toDegrees.js';
import { toRadians } from './util/toRadians.js';
import Vector2 from './Vector2.js';
import Vector3 from './Vector3.js';

Expand Down Expand Up @@ -50,20 +56,11 @@ const Utils = {
* @param {number} min
* @param {number} max
* @returns {number}
*
* NOTE: this function is deprecated - please use the separate file function directly, js/util/moduloBetweenDown.ts
*/
moduloBetweenDown( value, min, max ) {
assert && assert( max > min, 'max > min required for moduloBetween' );

const divisor = max - min;

// get a partial result of value-min between [0,divisor)
let partial = ( value - min ) % divisor;
if ( partial < 0 ) {
// since if value-min < 0, the remainder will give us a negative number
partial += divisor;
}

return partial + min; // add back in the minimum value
return moduloBetweenDown( value, min, max );
},

/**
Expand All @@ -77,9 +74,11 @@ const Utils = {
* @param {number} min
* @param {number} max
* @returns {number}
*
* NOTE: this function is deprecated - please use the separate file function directly, js/util/moduloBetweenUp.ts
*/
moduloBetweenUp( value, min, max ) {
return -Utils.moduloBetweenDown( -value, -max, -min );
return moduloBetweenUp( value, min, max );
},

/**
Expand All @@ -89,16 +88,11 @@ const Utils = {
* @param {number} a
* @param {number} b
* @returns {Array.<number>}
*
* NOTE: this function is deprecated - please use the separate file function directly, js/util/rangeInclusive.ts
*/
rangeInclusive( a, b ) {
if ( b < a ) {
return [];
}
const result = new Array( b - a + 1 );
for ( let i = a; i <= b; i++ ) {
result[ i - a ] = i;
}
return result;
return rangeInclusive( a, b );
},

/**
Expand All @@ -108,9 +102,11 @@ const Utils = {
* @param {number} a
* @param {number} b
* @returns {Array.<number>}
*
* NOTE: this function is deprecated - please use the separate file function directly, js/util/rangeExclusive.ts
*/
rangeExclusive( a, b ) {
return Utils.rangeInclusive( a + 1, b - 1 );
return rangeExclusive( a, b );
},

/**
Expand All @@ -119,9 +115,11 @@ const Utils = {
*
* @param {number} degrees
* @returns {number}
*
* NOTE: this function is deprecated - please use the separate file function directly, js/util/toRadians.ts
*/
toRadians( degrees ) {
return Math.PI * degrees / 180;
return toRadians( degrees );
},

/**
Expand All @@ -130,9 +128,11 @@ const Utils = {
*
* @param {number} radians
* @returns {number}
*
* NOTE: this function is deprecated - please use the separate file function directly, js/util/toDegrees.ts
*/
toDegrees( radians ) {
return 180 * radians / Math.PI;
return toDegrees( radians );
},

/**
Expand Down Expand Up @@ -895,13 +895,6 @@ const Utils = {
dot.register( 'Utils', Utils );

// make these available in the main namespace directly (for now)
dot.clamp = Utils.clamp;
dot.moduloBetweenDown = Utils.moduloBetweenDown;
dot.moduloBetweenUp = Utils.moduloBetweenUp;
dot.rangeInclusive = Utils.rangeInclusive;
dot.rangeExclusive = Utils.rangeExclusive;
dot.toRadians = Utils.toRadians;
dot.toDegrees = Utils.toDegrees;
dot.lineLineIntersection = Utils.lineLineIntersection;
dot.lineSegmentIntersection = Utils.lineSegmentIntersection;
dot.sphereRayIntersection = Utils.sphereRayIntersection;
Expand Down
7 changes: 4 additions & 3 deletions js/UtilsTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

import dot from './dot.js';
import { clamp } from './util/clamp.js';
import Utils from './Utils.js';
import Vector2 from './Vector2.js';

Expand Down Expand Up @@ -126,9 +127,9 @@ QUnit.test( 'linear map', assert => {
} );

QUnit.test( 'clamp', assert => {
assert.equal( Utils.clamp( 5, 1, 4 ), 4 );
assert.equal( Utils.clamp( 3, 1, 4 ), 3 );
assert.equal( Utils.clamp( 0, 1, 4 ), 1 );
assert.equal( clamp( 5, 1, 4 ), 4 );
assert.equal( clamp( 3, 1, 4 ), 3 );
assert.equal( clamp( 0, 1, 4 ), 1 );
} );

QUnit.test( 'rangeInclusive', assert => {
Expand Down
6 changes: 3 additions & 3 deletions js/Vector2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import IOType from '../../tandem/js/types/IOType.js';
import NumberIO from '../../tandem/js/types/NumberIO.js';
import { StateObject } from '../../tandem/js/types/StateSchema.js';
import dot from './dot.js';
import roundSymmetric from './util/roundSymmetric.js';
import { roundSymmetric } from './util/roundSymmetric.js';
import { clamp } from './util/clamp.js';

const ADDING_ACCUMULATOR = ( vector: Vector2, nextVector: Vector2 ) => {
return vector.add( nextVector );
Expand Down Expand Up @@ -126,8 +127,7 @@ export default class Vector2 implements TPoolable {
public angleBetween( v: Vector2 ): number {
const thisMagnitude = this.magnitude;
const vMagnitude = v.magnitude;
// @ts-expect-error TODO: import with circular protection https://github.com/phetsims/dot/issues/96
return Math.acos( dot.clamp( ( this.x * v.x + this.y * v.y ) / ( thisMagnitude * vMagnitude ), -1, 1 ) );
return Math.acos( clamp( ( this.x * v.x + this.y * v.y ) / ( thisMagnitude * vMagnitude ), -1, 1 ) );
}

/**
Expand Down
4 changes: 2 additions & 2 deletions js/Vector3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import Pool, { TPoolable } from '../../phet-core/js/Pool.js';
import IOType from '../../tandem/js/types/IOType.js';
import NumberIO from '../../tandem/js/types/NumberIO.js';
import dot from './dot.js';
import clamp from './util/clamp.js';
import roundSymmetric from './util/roundSymmetric.js';
import { clamp } from './util/clamp.js';
import { roundSymmetric } from './util/roundSymmetric.js';

const ADDING_ACCUMULATOR = ( vector: Vector3, nextVector: Vector3 ) => {
return vector.add( nextVector );
Expand Down
6 changes: 3 additions & 3 deletions js/Vector4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

import Pool, { TPoolable } from '../../phet-core/js/Pool.js';
import dot from './dot.js';
import roundSymmetric from './util/roundSymmetric.js';
import { clamp } from './util/clamp.js';
import { roundSymmetric } from './util/roundSymmetric.js';

export type Vector4StateObject = {
x: number;
Expand Down Expand Up @@ -129,8 +130,7 @@ export default class Vector4 implements TPoolable {
* is the input vector (normalized).
*/
public angleBetween( v: Vector4 ): number {
// @ts-expect-error TODO: import with circular protection https://github.com/phetsims/dot/issues/96
return Math.acos( dot.clamp( this.normalized().dot( v.normalized() ), -1, 1 ) );
return Math.acos( clamp( this.normalized().dot( v.normalized() ), -1, 1 ) );
}

/**
Expand Down
8 changes: 6 additions & 2 deletions js/util/clamp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
*
* @author Jonathan Olson <[email protected]>
*/
export default function clamp( value: number, min: number, max: number ): number {

import dot from '../dot.js';

export function clamp( value: number, min: number, max: number ): number {
if ( value < min ) {
return min;
}
Expand All @@ -16,4 +19,5 @@ export default function clamp( value: number, min: number, max: number ): number
else {
return value;
}
}
}
dot.register( 'clamp', clamp );
6 changes: 5 additions & 1 deletion js/util/distanceXY.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@
*
* @author Chris Malley ([email protected])
*/

import dot from '../dot.js';

export default function distanceXY( x1: number, y1: number, x2: number, y2: number ): number {
const dx = x1 - x2;
const dy = y1 - y2;
return Math.sqrt( dx * dx + dy * dy );
}
}
dot.register( 'distanceXY', distanceXY );
35 changes: 20 additions & 15 deletions js/util/factorial.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
// Copyright 2025, University of Colorado Boulder
// Copyright 2025, University of Colorado Boulder

/**
* Computes the factorial of a non-negative integer n without using recursion.
* n! = 1 * 2 * ... * ( n - 1 ) * n
*
* @author Chris Malley ([email protected])
*/
export default function factorial( n: number ): number {
assert && assert( Number.isInteger( n ) && n >= 0, `n must be a non-negative integer: ${n}` );
let f = 1;
for ( let i = 2; i <= n; i++ ) {
f *= i;
}
return f;
}
/**
* Computes the factorial of a non-negative integer n without using recursion.
* n! = 1 * 2 * ... * ( n - 1 ) * n
*
* @author Chris Malley ([email protected])
*/

import dot from '../dot.js';

export default function factorial( n: number ): number {
assert && assert( Number.isInteger( n ) && n >= 0, `n must be a non-negative integer: ${n}` );
let f = 1;
for ( let i = 2; i <= n; i++ ) {
f *= i;
}
return f;
}

dot.register( 'factorial', factorial );
28 changes: 28 additions & 0 deletions js/util/moduloBetweenDown.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2025, University of Colorado Boulder

/**
* Returns a number in the range $n\in[\mathrm{min},\mathrm{max})$ with the same equivalence class as the input
* value mod (max-min), i.e. for a value $m$, $m\equiv n\ (\mathrm{mod}\ \mathrm{max}-\mathrm{min})$.
*
* The 'down' indicates that if the value is equal to min or max, the max is returned.
*
* @author Jonathan Olson <[email protected]>
*/

import dot from '../dot.js';

export function moduloBetweenDown( value: number, min: number, max: number ): number {
assert && assert( max > min, 'max > min required for moduloBetween' );

const divisor = max - min;

// get a partial result of value-min between [0,divisor)
let partial = ( value - min ) % divisor;
if ( partial < 0 ) {
// since if value-min < 0, the remainder will give us a negative number
partial += divisor;
}

return partial + min; // add back in the minimum value
}
dot.register( 'moduloBetweenDown', moduloBetweenDown );
18 changes: 18 additions & 0 deletions js/util/moduloBetweenUp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2025, University of Colorado Boulder

/**
* Returns a number in the range $n\in(\mathrm{min},\mathrm{max}]$ with the same equivalence class as the input
* value mod (max-min), i.e. for a value $m$, $m\equiv n\ (\mathrm{mod}\ \mathrm{max}-\mathrm{min})$.
*
* The 'up' indicates that if the value is equal to min or max, the min is returned.
*
* @author Jonathan Olson <[email protected]>
*/

import { moduloBetweenDown } from './moduloBetweenDown.js';
import dot from '../dot.js';

export function moduloBetweenUp( value: number, min: number, max: number ): number {
return -moduloBetweenDown( -value, -max, -min );
}
dot.register( 'moduloBetweenUp', moduloBetweenUp );
15 changes: 15 additions & 0 deletions js/util/rangeExclusive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2025, University of Colorado Boulder

/**
* Returns an array of integers from A to B (exclusive), e.g. rangeExclusive( 4, 7 ) maps to [ 5, 6 ].
*
* @author Jonathan Olson <[email protected]>
*/

import { rangeInclusive } from './rangeInclusive.js';
import dot from '../dot.js';

export function rangeExclusive( a: number, b: number ): number[] {
return rangeInclusive( a + 1, b - 1 );
}
dot.register( 'rangeExclusive', rangeExclusive );
21 changes: 21 additions & 0 deletions js/util/rangeInclusive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2025, University of Colorado Boulder

/**
* Returns an array of integers from A to B (inclusive), e.g. rangeInclusive( 4, 7 ) maps to [ 4, 5, 6, 7 ].
*
* @author Jonathan Olson <[email protected]>
*/

import dot from '../dot.js';

export function rangeInclusive( a: number, b: number ): number[] {
if ( b < a ) {
return [];
}
const result = new Array( b - a + 1 );
for ( let i = a; i <= b; i++ ) {
result[ i - a ] = i;
}
return result;
}
dot.register( 'rangeInclusive', rangeInclusive );
Loading

0 comments on commit 10dd112

Please sign in to comment.