Open Andrew-Cottrell opened 4 years ago
An example implementation of the two suggested functions
/**
* @private
* @const {number}
*/
var TAU = 2 * Math.PI;
/**
* @private
* @param {number} turns - An angle in turns.
* @return {number} The same angle in the left-closed interval from -0.5 to +0.5.
*/
function turnPrincipalBranch( turns ) {
return turns - Math.floor( turns + 0.5 );
}
/**
* @param {number} angle - The angle expressed in some unit (default is decimal degrees).
* @param {number=} perigon - The number of units in a full rotation (default perigon is 360).
* @return {number} The same angle expressed in radians in the left-closed interval from -π to +π.
*/
Math.toRadians = function ( angle, perigon ) {
return turnPrincipalBranch( angle / ( perigon || 360 ) ) * TAU;
};
/**
* @param {number} radians - The angle expressed in radians.
* @param {number=} perigon - The number of units in a full rotation (default perigon is 360).
* @return {number} The same angle expressed in some unit (default is decimal degrees).
*/
Math.fromRadians = function ( radians, perigon ) {
return turnPrincipalBranch( radians / TAU ) * ( perigon || 360 );
};
Before I realized this proposal existed (and this specific Issue) I was using these polyfills:
/**
converts degrees to radians by default
@param {number} x
@param {number} [y=360] the input scale
@return {number}
*/
Math.angleToRad = function(x, y = 360) {return TAU / +y * +x}
//scale = 360: degrees
//scale = 1: Tau radians
/**
converts radians to degrees by default
@param {number} x
@param {number} [y=360] the output scale
@return {number}
*/
Math.radToAngle = function(x, y = 360) {return +x / (TAU / +y)}
//unary plus is used to replicate the exact error type as the built-in Math methods
But I think the naming toRadians
and fromRadians
seems better. I just wanted to show the code to give more ideas and inspiration for other names, just in case they're changed
Wait, is turnPrincipalBranch
calculating the normalized version of the angle? If so, what are the reasons why it might be desirable? I think the function shouldn't normalize angles unless explicitly specified by the user/dev. We could add a 3rd bool arg, or just add the normalizer as a standalone Math
method
Wait, is
turnPrincipalBranch
calculating the normalized version of the angle? If so, what are the reasons why it might be desirable?
As mentioned in my opening comment above
As latitude and longitude are often expressed as decimal degrees in the intervals
[−90°, +90°]
and[−180°, +180°]
respectively, in my implementation I choose to ensure all return values are in an interval[-perigon/2, +perigon/2]
(similar toMath.asin
andMath.atan
). This reduces surprises and may help maintain precision across multiple floating-point operations. This convention equates an angle in radians with the directed minor arc from one point to another on the unit circle, which is useful in some distance calculations.With a suitable modulo operation (https://github.com/rwaldron/proposal-math-extensions/issues/21), it is simple for calling code to convert from
[-perigon/2, +perigon/2]
to[0, perigon]
if needed.
However, this is simply a choice I made in my implementation; a different choice could be made.
Aside: my use of the term "principal branch" may not be strictly accurate. If we wanted to consider θ
and θ + τ
as distinct angles then we're kinda getting a Riemann surface where concepts like branch cuts and principal branches apply (like with complex logarithms). This is quite advanced stuff and I guess most people would consider θ
and θ + τ
to be the same angle (like 6/4
and 3/2
are the same fraction). I can't think of any use cases that would depend on un-normalized angles.
Seems good to me
I have found the following two functions to be applicable in more use cases (https://github.com/rwaldron/proposal-math-extensions/issues/5#issuecomment-491365090) than the two proposed functions,
Math.radians
andMath.degrees
Math.toRadians( angle, perigon = 360 )
//
displacement
&litude
and/ordistance
&wavelength
may be in units of pixel var displacement = amplitude * Math.sin( Math.toRadians( distance, wavelength ) );var radians = Math.toRadians( turns, 1 ); var radians = Math.toRadians( decimalDegrees ); // default perigon is 360 var radians = Math.toRadians( arcminutes + 60 degrees, 60 360 ); var radians = Math.toRadians( arcseconds + 60 ( arcminutes + 60 degrees ), 60 60 360 ); var radians = Math.toRadians( gradians, 400 ); var radians = Math.toRadians( milliturns, 1000 ); var radians = Math.toRadians( binaryAngle, 256 );
As latitude and longitude are often expressed as decimal degrees in the intervals
[−90°, +90°]
and[−180°, +180°]
respectively, in my implementation I choose to ensure all return values are in an interval[-perigon/2, +perigon/2]
(similar toMath.asin
andMath.atan
). This reduces surprises and may help maintain precision across multiple floating-point operations. This convention equates an angle in radians with the directed minor arc from one point to another on the unit circle, which is useful in some distance calculations.With a suitable modulo operation (#21), it is simple for calling code to convert from
[-perigon/2, +perigon/2]
to[0, perigon]
if neededI have found that some JavaScript developers do not know the built-in trigonometry functions operate on angles expressed in radians. They assume, perhaps due to previous experience with calculators defaulting to DEG mode, that the following are valid
Perhaps the presence of
Math.toRadians
&Math.fromRadians
, and the absence of any function (in theMath
namespace) with "degrees" in its name would help them to realise the built-in trigonometry functions expect angles to be expressed in radians.