/* * This program source code file is part of KICAD, a free EDA CAD application. * * Copyright (C) 2010 Virtenio GmbH, Torsten Hueter, torsten.hueter virtenio.de * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 2012-2021 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2013 CERN * @author Tomasz Wlostowski * * 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 2 * 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, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #ifndef VECTOR2D_H_ #define VECTOR2D_H_ #include #include #include #include #include /** * Traits class for VECTOR2. */ template struct VECTOR2_TRAITS { /// extended range/precision types used by operations involving multiple /// multiplications to prevent overflow. typedef T extended_type; }; template <> struct VECTOR2_TRAITS { typedef int64_t extended_type; }; // Forward declarations for template friends template class VECTOR2; template std::ostream& operator<<( std::ostream& aStream, const VECTOR2& aVector ); /** * Define a general 2D-vector/point. * * This class uses templates to be universal. Several operators are provided to help * easy implementing of linear algebra equations. * */ template class VECTOR2 { public: typedef typename VECTOR2_TRAITS::extended_type extended_type; typedef T coord_type; static constexpr extended_type ECOORD_MAX = std::numeric_limits::max(); static constexpr extended_type ECOORD_MIN = std::numeric_limits::min(); T x, y; /// Construct a 2D-vector with x, y = 0 VECTOR2(); /// Construct a vector with given components x, y VECTOR2( T x, T y ); /// Initializes a vector from another specialization. Beware of rounding issues. template VECTOR2( const VECTOR2& aVec ) { if( std::is_floating_point() ) { x = static_cast( aVec.x ); y = static_cast( aVec.y ); } else if( std::is_floating_point() ) { CastingType minI = static_cast( std::numeric_limits::min() ); CastingType maxI = static_cast( std::numeric_limits::max() ); x = static_cast( Clamp( minI, aVec.x, maxI ) ); y = static_cast( Clamp( minI, aVec.y, maxI ) ); } else if( std::is_integral() && std::is_integral() ) { int64_t minI = static_cast( std::numeric_limits::min() ); int64_t maxI = static_cast( std::numeric_limits::max() ); x = static_cast( Clamp( minI, static_cast( aVec.x ), maxI ) ); y = static_cast( Clamp( minI, static_cast(aVec.y), maxI ) ); } else { x = static_cast( aVec.x ); y = static_cast( aVec.y ); } } /// Copy a vector VECTOR2( const VECTOR2& aVec ) { x = aVec.x; y = aVec.y; } /// Cast a vector to another specialized subclass. Beware of rounding issues. template VECTOR2 operator()() const { if( std::is_floating_point::value ) { return VECTOR2( static_cast( x ), static_cast( y ) ); } else if( std::is_floating_point() ) { T minI = static_cast( std::numeric_limits::min() ); T maxI = static_cast( std::numeric_limits::max() ); return VECTOR2( static_cast( Clamp( minI, x, maxI ) ), static_cast( Clamp( minI, y, maxI ) ) ); } else if( std::is_integral() && std::is_integral() ) { int64_t minI = static_cast( std::numeric_limits::min() ); int64_t maxI = static_cast( std::numeric_limits::max() ); return VECTOR2( static_cast( Clamp( minI, static_cast( x ), maxI ) ), static_cast( Clamp( minI, static_cast( y ), maxI ) ) ); } else { return VECTOR2( static_cast( x ), static_cast( y ) ); } } // virtual ~VECTOR2(); /** * Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2). * * It is used to calculate the length of the vector. * * @return Scalar, the euclidean norm */ T EuclideanNorm() const; /** * Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2). * * It is used to calculate the length of the vector. * * @return Scalar, the euclidean norm */ extended_type SquaredEuclideanNorm() const; /** * Compute the perpendicular vector. * * @return Perpendicular vector */ VECTOR2 Perpendicular() const; /** * Return a vector of the same direction, but length specified in \a aNewLength. * * @param aNewLength is the length of the rescaled vector. * @return the rescaled vector. */ VECTOR2 Resize( T aNewLength ) const; /** * Return the vector formatted as a string. * * @return the formatted string */ const std::string Format() const; /** * Compute cross product of self with \a aVector. */ extended_type Cross( const VECTOR2& aVector ) const; /** * Compute dot product of self with \a aVector. */ extended_type Dot( const VECTOR2& aVector ) const; /** * Compute the distance between two vectors. This is a double precision * value because the distance is frequently non-integer. */ double Distance( const VECTOR2& aVector ) const; // Operators /// Assignment operator VECTOR2& operator=( const VECTOR2& aVector ); /// Compound assignment operator VECTOR2& operator+=( const VECTOR2& aVector ); /// Compound assignment operator VECTOR2& operator*=( const VECTOR2& aVector ); VECTOR2& operator*=( const T& aScalar ); /// Compound assignment operator VECTOR2& operator+=( const T& aScalar ); /// Compound assignment operator VECTOR2& operator-=( const VECTOR2& aVector ); /// Compound assignment operator VECTOR2& operator-=( const T& aScalar ); /// Negate Vector operator VECTOR2 operator-(); /// Division with a factor VECTOR2 operator/( double aFactor ) const; /// Equality operator bool operator==( const VECTOR2& aVector ) const; /// Not equality operator bool operator!=( const VECTOR2& aVector ) const; /// Smaller than operator bool operator<( const VECTOR2& aVector ) const; bool operator<=( const VECTOR2& aVector ) const; /// Greater than operator bool operator>( const VECTOR2& aVector ) const; bool operator>=( const VECTOR2& aVector ) const; }; // ---------------------- // --- Implementation --- // ---------------------- template VECTOR2::VECTOR2() : x{}, y{} { } template VECTOR2::VECTOR2( T aX, T aY ) { x = aX; y = aY; } template T VECTOR2::EuclideanNorm() const { // 45° are common in KiCad, so we can optimize the calculation if( std::abs( x ) == std::abs( y ) ) { if( std::is_integral::value ) return KiROUND( std::abs( x ) * M_SQRT2 ); return static_cast( std::abs( x ) * M_SQRT2 ); } if( x == 0 ) return static_cast( std::abs( y ) ); if( y == 0 ) return static_cast( std::abs( x ) ); if( std::is_integral::value ) return KiROUND( std::hypot( x, y ) ); return static_cast( std::hypot( x, y ) ); } template typename VECTOR2::extended_type VECTOR2::SquaredEuclideanNorm() const { return (extended_type) x * x + (extended_type) y * y; } template VECTOR2 VECTOR2::Perpendicular() const { VECTOR2 perpendicular( -y, x ); return perpendicular; } template VECTOR2& VECTOR2::operator=( const VECTOR2& aVector ) { x = aVector.x; y = aVector.y; return *this; } template VECTOR2& VECTOR2::operator+=( const VECTOR2& aVector ) { x += aVector.x; y += aVector.y; return *this; } template VECTOR2& VECTOR2::operator*=( const VECTOR2& aVector ) { x *= aVector.x; y *= aVector.y; return *this; } template VECTOR2& VECTOR2::operator*=( const T& aScalar ) { x *= aScalar; y *= aScalar; return *this; } template VECTOR2& VECTOR2::operator+=( const T& aScalar ) { x += aScalar; y += aScalar; return *this; } template VECTOR2& VECTOR2::operator-=( const VECTOR2& aVector ) { x -= aVector.x; y -= aVector.y; return *this; } template VECTOR2& VECTOR2::operator-=( const T& aScalar ) { x -= aScalar; y -= aScalar; return *this; } template VECTOR2 VECTOR2::Resize( T aNewLength ) const { if( x == 0 && y == 0 ) return VECTOR2 ( 0, 0 ); double newX; double newY; if( std::abs( x ) == std::abs( y ) ) { newX = newY = std::abs( aNewLength ) * M_SQRT1_2; } else { extended_type x_sq = (extended_type) x * x; extended_type y_sq = (extended_type) y * y; extended_type l_sq = x_sq + y_sq; extended_type newLength_sq = (extended_type) aNewLength * aNewLength; newX = std::sqrt( rescale( newLength_sq, x_sq, l_sq ) ); newY = std::sqrt( rescale( newLength_sq, y_sq, l_sq ) ); } if( std::is_integral::value ) { return VECTOR2( static_cast( x < 0 ? -KiROUND( newX ) : KiROUND( newX ) ), static_cast( y < 0 ? -KiROUND( newY ) : KiROUND( newY ) ) ) * sign( aNewLength ); } else { return VECTOR2( static_cast( x < 0 ? -newX : newX ), static_cast( y < 0 ? -newY : newY ) ) * sign( aNewLength ); } } template const std::string VECTOR2::Format() const { std::stringstream ss; ss << "( xy " << x << " " << y << " )"; return ss.str(); } template concept FloatingPoint = std::is_floating_point::value; template concept Integral = std::is_integral::value; template VECTOR2> operator+( const VECTOR2& aLHS, const VECTOR2& aRHS ) { return VECTOR2>( aLHS.x + aRHS.x, aLHS.y + aRHS.y ); } template VECTOR2 operator+( const VECTOR2& aLHS, const U& aScalar ) { return VECTOR2( aLHS.x + aScalar, aLHS.y + aScalar ); } template VECTOR2 operator+( const VECTOR2& aLHS, const U& aScalar ) { return VECTOR2( aLHS.x + aScalar, aLHS.y + aScalar ); } template VECTOR2 operator+( const VECTOR2& aLHS, const U& aScalar ) { return VECTOR2( KiROUND( aLHS.x + aScalar ), KiROUND( aLHS.y + aScalar ) ); } template VECTOR2> operator-( const VECTOR2& aLHS, const VECTOR2& aRHS ) { return VECTOR2>( aLHS.x - aRHS.x, aLHS.y - aRHS.y ); } template VECTOR2 operator-( const VECTOR2& aLHS, U aScalar ) { return VECTOR2( aLHS.x - aScalar, aLHS.y - aScalar ); } template VECTOR2 operator-( const VECTOR2& aLHS, U aScalar ) { return VECTOR2( aLHS.x - aScalar, aLHS.y - aScalar ); } template VECTOR2 operator-( const VECTOR2& aLHS, const U& aScalar ) { return VECTOR2( KiROUND( aLHS.x - aScalar ), KiROUND( aLHS.y - aScalar ) ); } template VECTOR2 VECTOR2::operator-() { return VECTOR2 ( -x, -y ); } template #ifdef SWIG double operator*( const VECTOR2& aLHS, const VECTOR2& aRHS ) #else auto operator*( const VECTOR2& aLHS, const VECTOR2& aRHS ) #endif { using extended_type = typename VECTOR2>::extended_type; return (extended_type)aLHS.x * aRHS.x + (extended_type)aLHS.y * aRHS.y; } template VECTOR2> operator*( const VECTOR2& aLHS, const U& aScalar ) { return VECTOR2>( aLHS.x * aScalar, aLHS.y * aScalar ); } template VECTOR2> operator*( const T& aScalar, const VECTOR2& aVector ) { return VECTOR2>( aScalar * aVector.x, aScalar * aVector.y ); } template VECTOR2 VECTOR2::operator/( double aFactor ) const { if( std::is_integral::value ) return VECTOR2( KiROUND( x / aFactor ), KiROUND( y / aFactor ) ); else return VECTOR2( static_cast( x / aFactor ), static_cast( y / aFactor ) ); } template typename VECTOR2::extended_type VECTOR2::Cross( const VECTOR2& aVector ) const { return (extended_type) x * (extended_type) aVector.y - (extended_type) y * (extended_type) aVector.x; } template typename VECTOR2::extended_type VECTOR2::Dot( const VECTOR2& aVector ) const { return (extended_type) x * (extended_type) aVector.x + (extended_type) y * (extended_type) aVector.y; } template double VECTOR2::Distance( const VECTOR2& aVector ) const { VECTOR2 diff( aVector.x - x, aVector.y - y ); return diff.EuclideanNorm(); } template bool VECTOR2::operator<( const VECTOR2& aVector ) const { return ( *this * *this ) < ( aVector * aVector ); } template bool VECTOR2::operator<=( const VECTOR2& aVector ) const { return ( *this * *this ) <= ( aVector * aVector ); } template bool VECTOR2::operator>( const VECTOR2& aVector ) const { return ( *this * *this ) > ( aVector * aVector ); } template bool VECTOR2::operator>=( const VECTOR2& aVector ) const { return ( *this * *this ) >= ( aVector * aVector ); } template bool VECTOR2::operator==( VECTOR2 const& aVector ) const { return ( aVector.x == x ) && ( aVector.y == y ); } template bool VECTOR2::operator!=( VECTOR2 const& aVector ) const { return ( aVector.x != x ) || ( aVector.y != y ); } template const VECTOR2 LexicographicalMax( const VECTOR2& aA, const VECTOR2& aB ) { if( aA.x > aB.x ) return aA; else if( aA.x == aB.x && aA.y > aB.y ) return aA; return aB; } template const VECTOR2 LexicographicalMin( const VECTOR2& aA, const VECTOR2& aB ) { if( aA.x < aB.x ) return aA; else if( aA.x == aB.x && aA.y < aB.y ) return aA; return aB; } template int LexicographicalCompare( const VECTOR2& aA, const VECTOR2& aB ) { if( aA.x < aB.x ) return -1; else if( aA.x > aB.x ) return 1; else // aA.x == aB.x { if( aA.y < aB.y ) return -1; else if( aA.y > aB.y ) return 1; else return 0; } } /** * Template to compare two VECTOR2 values for equality within a required epsilon. * * @param aFirst value to compare. * @param aSecond value to compare. * @param aEpsilon allowed error. * @return true if the values considered equal within the specified epsilon, otherwise false. */ template typename std::enable_if::is_integer, bool>::type equals( VECTOR2 const& aFirst, VECTOR2 const& aSecond, T aEpsilon = std::numeric_limits::epsilon() ) { if( !equals( aFirst.x, aSecond.x, aEpsilon ) ) { return false; } return equals( aFirst.y, aSecond.y, aEpsilon ); } template std::ostream& operator<<( std::ostream& aStream, const VECTOR2& aVector ) { aStream << "[ " << aVector.x << " | " << aVector.y << " ]"; return aStream; } /* Default specializations */ typedef VECTOR2 VECTOR2D; typedef VECTOR2 VECTOR2I; typedef VECTOR2 VECTOR2L; /* KiROUND specialization for vectors */ inline VECTOR2I KiROUND( const VECTOR2D& vec ) { return VECTOR2I( KiROUND( vec.x ), KiROUND( vec.y ) ); } /* STL specializations */ namespace std { // Required to enable correct use in std::map/unordered_map // DO NOT USE hash tables with VECTOR2 elements. It is inefficient // and degenerates to a linear search. Use the std::map/std::set // trees instead that utilize the less operator below // This function is purposely deleted after substantial testing template <> struct hash { size_t operator()( const VECTOR2I& k ) const = delete; }; // Required to enable use of std::hash with maps. template <> struct less { bool operator()( const VECTOR2I& aA, const VECTOR2I& aB ) const; }; } #endif // VECTOR2D_H_