kicad/libs/kimath/include/geometry/eda_angle.h

439 lines
11 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021-2022 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EDA_ANGLE_H
#define EDA_ANGLE_H
#include <cassert>
#include <cmath>
#include <math/vector2d.h> // for VECTOR2I
enum EDA_ANGLE_T
{
TENTHS_OF_A_DEGREE_T,
DEGREES_T,
RADIANS_T
};
class EDA_ANGLE
{
public:
/**
* Angles can be created in degrees, 1/10ths of a degree, or radians, and read as any of
* the angle types.
*
* Angle type must be explicitly specified at creation, because there is no other way of
* knowing what an int or a double represents.
*/
EDA_ANGLE( double aValue, EDA_ANGLE_T aAngleType )
{
switch( aAngleType )
{
case RADIANS_T:
m_value = aValue / DEGREES_TO_RADIANS;
break;
case TENTHS_OF_A_DEGREE_T:
m_value = aValue / 10.0;
break;
default:
m_value = aValue;
}
}
explicit EDA_ANGLE( const VECTOR2D& aVector )
{
if( aVector.x == 0.0 && aVector.y == 0.0 )
{
m_value = 0.0;
}
else if( aVector.y == 0.0 )
{
if( aVector.x >= 0 )
m_value = 0.0;
else
m_value = -180.0;
}
else if( aVector.x == 0.0 )
{
if( aVector.y >= 0.0 )
m_value = 90.0;
else
m_value = -90.0;
}
else if( aVector.x == aVector.y )
{
if( aVector.x >= 0.0 )
m_value = 45.0;
else
m_value = -180.0 + 45.0;
}
else if( aVector.x == -aVector.y )
{
if( aVector.x >= 0.0 )
m_value = -45.0;
else
m_value = 180.0 - 45.0;
}
else
{
*this = EDA_ANGLE( atan2( aVector.y, aVector.x ), RADIANS_T );
}
}
explicit EDA_ANGLE( const VECTOR2I& aVector )
{
/* gcc is surprisingly smart in optimizing these conditions in a tree! */
if( aVector.x == 0 && aVector.y == 0 )
{
m_value = 0;
}
else if( aVector.y == 0 )
{
if( aVector.x >= 0 )
m_value = 0.0;
else
m_value = -180.0;
}
else if( aVector.x == 0 )
{
if( aVector.y >= 0 )
m_value = 90.0;
else
m_value = -90.0;
}
else if( aVector.x == aVector.y )
{
if( aVector.x >= 0 )
m_value = 45.0;
else
m_value = -180.0 + 45.0;
}
else if( aVector.x == -aVector.y )
{
if( aVector.x >= 0 )
m_value = -45.0;
else
m_value = 180.0 - 45.0;
}
else
{
*this = EDA_ANGLE( atan2( (double) aVector.y, (double) aVector.x ), RADIANS_T );
}
}
EDA_ANGLE() :
m_value( 0.0 )
{}
inline double AsDegrees() const { return m_value; }
inline int AsTenthsOfADegree() const { return KiROUND( m_value * 10.0 ); }
inline double AsRadians() const { return m_value * DEGREES_TO_RADIANS; }
static constexpr double DEGREES_TO_RADIANS = M_PI / 180.0;
/**
* @return true if angle is one of the four cardinal directions (0/90/180/270 degrees),
* otherwise false
*/
bool IsCardinal() const;
/**
* @return true if angle is one of the two cardinal directions (90/270 degrees),
* otherwise false
*/
bool IsCardinal90() const;
bool IsZero() const
{
return m_value == 0.0;
}
bool IsHorizontal() const
{
return m_value == 0.0 || m_value == 180.0;
}
bool IsVertical() const
{
return m_value == 90.0 || m_value == 270.0;
}
bool IsParallelTo( EDA_ANGLE aAngle ) const
{
EDA_ANGLE thisNormalized = *this;
// Normalize90 is inclusive on both ends [-90, +90]
// but we need it to be (-90, +90] for this test to work
thisNormalized.Normalize90();
if( thisNormalized.AsDegrees() == -90.0 )
thisNormalized = EDA_ANGLE( 90.0, DEGREES_T );
aAngle.Normalize90();
if( aAngle.AsDegrees() == -90.0 )
aAngle = EDA_ANGLE( 90.0, DEGREES_T );
return ( thisNormalized.AsDegrees() == aAngle.AsDegrees() );
}
EDA_ANGLE Invert() const
{
return EDA_ANGLE( -AsDegrees(), DEGREES_T );
}
double Sin() const
{
EDA_ANGLE test = *this;
test.Normalize();
if( test.m_value == 0.0 || test.m_value == 180.0 )
return 0.0;
else if( test.m_value == 90.0 )
return 1.0;
else if( test.m_value == 270.0 )
return -1.0;
else
return sin( AsRadians() );
}
double Cos() const
{
EDA_ANGLE test = *this;
test.Normalize();
if( test.m_value == 0.0 )
return 1.0;
else if( test.m_value == 180.0 )
return -1.0;
else if( test.m_value == 90.0 || test.m_value == 270.0 )
return 0.0;
else
return cos( AsRadians() );
}
double Tan() const { return tan( AsRadians() ); }
static EDA_ANGLE Arccos( double x ) { return EDA_ANGLE( acos( x ), RADIANS_T ); }
static EDA_ANGLE Arcsin( double x ) { return EDA_ANGLE( asin( x ), RADIANS_T ); }
static EDA_ANGLE Arctan( double x ) { return EDA_ANGLE( atan( x ), RADIANS_T ); }
static EDA_ANGLE Arctan2( double y, double x )
{
return EDA_ANGLE( atan2( y, x ), RADIANS_T );
}
inline EDA_ANGLE Normalize()
{
while( m_value < -0.0 )
m_value += 360.0;
while( m_value >= 360.0 )
m_value -= 360.0;
return *this;
}
EDA_ANGLE Normalized() const
{
EDA_ANGLE ret( *this );
return ret.Normalize();
}
inline EDA_ANGLE NormalizeNegative()
{
while( m_value <= -360.0 )
m_value += 360.0;
while( m_value > 0.0 )
m_value -= 360.0;
return *this;
}
inline EDA_ANGLE Normalize90()
{
while( m_value < -90.0 )
m_value += 180.0;
while( m_value > 90.0 )
m_value -= 180.0;
return *this;
}
inline EDA_ANGLE Normalize180()
{
while( m_value <= -180.0 )
m_value += 360.0;
while( m_value > 180.0 )
m_value -= 360.0;
return *this;
}
inline EDA_ANGLE Normalize720()
{
while( m_value < -360.0 )
m_value += 360.0;
while( m_value >= 360.0 )
m_value -= 360.0;
return *this;
}
EDA_ANGLE KeepUpright() const;
EDA_ANGLE& operator+=( const EDA_ANGLE& aAngle )
{
*this = EDA_ANGLE( AsDegrees() + aAngle.AsDegrees(), DEGREES_T );
return *this;
}
EDA_ANGLE& operator-=( const EDA_ANGLE& aAngle )
{
*this = EDA_ANGLE( AsDegrees() - aAngle.AsDegrees(), DEGREES_T );
return *this;
}
private:
double m_value; ///< value in degrees
public:
static EDA_ANGLE m_Angle0;
static EDA_ANGLE m_Angle45;
static EDA_ANGLE m_Angle90;
static EDA_ANGLE m_Angle135;
static EDA_ANGLE m_Angle180;
static EDA_ANGLE m_Angle270;
static EDA_ANGLE m_Angle360;
};
inline EDA_ANGLE operator-( const EDA_ANGLE& aAngle )
{
return aAngle.Invert();
}
inline EDA_ANGLE operator-( const EDA_ANGLE& aAngleA, const EDA_ANGLE& aAngleB )
{
return EDA_ANGLE( aAngleA.AsDegrees() - aAngleB.AsDegrees(), DEGREES_T );
}
inline EDA_ANGLE operator+( const EDA_ANGLE& aAngleA, const EDA_ANGLE& aAngleB )
{
return EDA_ANGLE( aAngleA.AsDegrees() + aAngleB.AsDegrees(), DEGREES_T );
}
inline EDA_ANGLE operator*( const EDA_ANGLE& aAngleA, double aOperator )
{
return EDA_ANGLE( aAngleA.AsDegrees() * aOperator, DEGREES_T );
}
inline EDA_ANGLE operator/( const EDA_ANGLE& aAngleA, double aOperator )
{
return EDA_ANGLE( aAngleA.AsDegrees() / aOperator, DEGREES_T );
}
inline double operator/( const EDA_ANGLE& aAngleA, EDA_ANGLE& aOperator )
{
return aAngleA.AsDegrees() / aOperator.AsDegrees();
}
inline bool operator==( const EDA_ANGLE& aAngleA, const EDA_ANGLE& aAngleB )
{
return aAngleA.AsDegrees() == aAngleB.AsDegrees();
}
inline bool operator!=( const EDA_ANGLE& aAngleA, const EDA_ANGLE& aAngleB )
{
return aAngleA.AsDegrees() != aAngleB.AsDegrees();
}
inline bool operator>( const EDA_ANGLE& aAngleA, const EDA_ANGLE& aAngleB )
{
return aAngleA.AsDegrees() > aAngleB.AsDegrees();
}
inline bool operator<( const EDA_ANGLE& aAngleA, const EDA_ANGLE& aAngleB )
{
return aAngleA.AsDegrees() < aAngleB.AsDegrees();
}
inline bool operator<=( const EDA_ANGLE& aAngleA, const EDA_ANGLE& aAngleB )
{
return aAngleA.AsDegrees() <= aAngleB.AsDegrees();
}
inline bool operator>=( const EDA_ANGLE& aAngleA, const EDA_ANGLE& aAngleB )
{
return aAngleA.AsDegrees() >= aAngleB.AsDegrees();
}
inline std::ostream& operator<<( std::ostream& aStream, const EDA_ANGLE& aAngle )
{
return aStream << aAngle.AsDegrees();
}
namespace std
{
inline EDA_ANGLE abs( const EDA_ANGLE& aAngle )
{
return EDA_ANGLE( std::abs( aAngle.AsDegrees() ), DEGREES_T );
}
}
static constexpr EDA_ANGLE& ANGLE_HORIZONTAL = EDA_ANGLE::m_Angle0;
static constexpr EDA_ANGLE& ANGLE_VERTICAL = EDA_ANGLE::m_Angle90;
static constexpr EDA_ANGLE& FULL_CIRCLE = EDA_ANGLE::m_Angle360;
static constexpr EDA_ANGLE& ANGLE_0 = EDA_ANGLE::m_Angle0;
static constexpr EDA_ANGLE& ANGLE_45 = EDA_ANGLE::m_Angle45;
static constexpr EDA_ANGLE& ANGLE_90 = EDA_ANGLE::m_Angle90;
static constexpr EDA_ANGLE& ANGLE_135 = EDA_ANGLE::m_Angle135;
static constexpr EDA_ANGLE& ANGLE_180 = EDA_ANGLE::m_Angle180;
static constexpr EDA_ANGLE& ANGLE_270 = EDA_ANGLE::m_Angle270;
static constexpr EDA_ANGLE& ANGLE_360 = EDA_ANGLE::m_Angle360;
#endif // EDA_ANGLE_H