Skip to content

Instantly share code, notes, and snippets.

@Const-me
Last active April 7, 2018 04:04
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Const-me/46f90ce7fc2bc65dc12a41442ed51f73 to your computer and use it in GitHub Desktop.
Save Const-me/46f90ce7fc2bc65dc12a41442ed51f73 to your computer and use it in GitHub Desktop.
#define _USE_MATH_DEFINES
#include <math.h>
struct Point
{
double x, y;
};
// A class that contains a 2D angle as a pair of sine + cosine values.
// Sometimes, this approach saves substantial CPU time that would be wasted running these trigonometry functions.
// While reasonably efficient, one downside of this approach is it's hard to implement multiplication by a scalar value, i.e. linear interpolation. If that's your case, use quaternions instead, they work fine for 2D.
class Angle
{
double x; // = cos(angle)
double y; // = sin(angle)
// Private constructor from raw values. Parameters must be normalized already.
Angle( double _x, double _y ) : x( _x ), y( _y ) {}
public:
// Construct from radians
Angle( double rad )
{
x = cos( rad );
y = sin( rad );
}
// Construct from a radius vector, by normalizing
Angle( const Point &pt )
{
double mul = 1.0 / sqrt( pt.x * pt.x + pt.y * pt.y );
x = pt.x * mul;
y = pt.y * mul;
}
Angle( const Angle& that ) = default;
Angle& operator=( const Angle& that ) = default;
// Divide the angle by 2. The input angle is assumed to be between -Pi and +Pi. To inperpret the angle to be between 0 and 2*Pi, flip the result when y < 0
Angle half() const
{
// We do that by adding { 1, 0 } vector, and normalizing the result back onto the unit circle. This way it's a single square root, compared to 2 roots when using classical formulae.
const double x2 = x + 1.0;
const double len = sqrt( x2*x2 + y*y );
if( isnormal( len ) )
{
const double mul = 1.0 / len;
return Angle{ x2 * mul, y * mul };
}
// The length of the result is a de-normalized float, i.e. very close to 0. This means the angle is very close to Pi. Therefore, the result is very close to pi/2
// All 3 exact zeros, -0.0, 0, and +0.0, are not normal numbers according to isnormal API, which is exactly what we want in this case.
return Angle{ 0, ( y >= 0 ) ? 1.0 : -1.0 };
}
// Flip the angle, it's the same as adding Pi to it.
Angle operator-() const
{
return Angle{ -x, -y };
}
// Multiply the angle by 2
Angle dbl() const
{
return Angle{ x*x - y*y, 2 * x*y };
}
// Add angles
Angle operator+( const Angle &that ) const
{
return Angle{ x*that.x - y*that.y, y*that.x + x*that.y };
}
// Subtract angles
Angle operator-( const Angle &that ) const
{
return Angle{ x*that.x + y*that.y, y*that.x - x*that.y };
}
// A point on circle with specified radius
Point radius( double r ) const
{
return Point{ x*r, y*r };
}
// A point on circle with specified radius and circle center
Point radius( const Point &center, double r ) const
{
return Point{ center.x + x*r, center.y + y*r };
}
static Angle zero() { return Angle{ 1, 0 }; }
static Angle pi() { return Angle{ -1, 0 }; }
static Angle halfPi() { return Angle{ 0, 1 }; }
};
int main()
{
Angle x( 7 * M_PI / 8 );
Angle y = x.half();
Angle y2( 7 * M_PI / 16 );
Angle x2 = y + y;
Angle x3 = y.dbl();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment