blob: 8e93cbfd6e519d4a62267ad7dbed3e193a2881fa [file] [log] [blame]
 // Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) // Needed on Windows to get |M_PI| from math.h. #ifdef _WIN32 #define _USE_MATH_DEFINES #endif #ifndef S2_S1ANGLE_H_ #define S2_S1ANGLE_H_ #include #include #include #include #include "s2/_fpcontractoff.h" #include "s2/util/math/mathutil.h" #include "s2/util/math/vector.h" class S2LatLng; // Avoid circular include of s2.h. // This must be a typedef rather than a type alias declaration because of SWIG. typedef Vector3_d S2Point; // This class represents a one-dimensional angle (as opposed to a // two-dimensional solid angle). It has methods for converting angles to // or from radians, degrees, and the E5/E6/E7 representations (i.e. degrees // multiplied by 1e5/1e6/1e7 and rounded to the nearest integer). // // The internal representation is a double-precision value in radians, so // conversion to and from radians is exact. Conversions between E5, E6, E7, // and Degrees are not always exact; for example, Degrees(3.1) is different // from E6(3100000) or E7(310000000). However, the following properties are // guaranteed for any integer "n", provided that "n" is in the input range of // both functions: // // Degrees(n) == E6(1000000 * n) // Degrees(n) == E7(10000000 * n) // E6(n) == E7(10 * n) // // The corresponding properties are *not* true for E5, so if you use E5 then // don't test for exact equality when comparing to other formats such as // Degrees or E7. // // The following conversions between degrees and radians are exact: // // Degrees(180) == Radians(M_PI) // Degrees(45 * k) == Radians(k * M_PI / 4) for k == 0..8 // // These identities also hold when the arguments are scaled up or down by any // power of 2. Some similar identities are also true, for example, // Degrees(60) == Radians(M_PI / 3), but be aware that this type of identity // does not hold in general. For example, Degrees(3) != Radians(M_PI / 60). // // Similarly, the conversion to radians means that Angle::Degrees(x).degrees() // does not always equal "x". For example, // // S1Angle::Degrees(45 * k).degrees() == 45 * k for k == 0..8 // but S1Angle::Degrees(60).degrees() != 60. // // This means that when testing for equality, you should allow for numerical // errors (EXPECT_DOUBLE_EQ) or convert to discrete E5/E6/E7 values first. // // CAVEAT: All of the above properties depend on "double" being the usual // 64-bit IEEE 754 type (which is true on almost all modern platforms). // // This class is intended to be copied by value as desired. It uses // the default copy constructor and assignment operator. class S1Angle { public: // These methods construct S1Angle objects from their measure in radians // or degrees. static constexpr S1Angle Radians(double radians); static constexpr S1Angle Degrees(double degrees); static constexpr S1Angle E5(int32_t e5); static constexpr S1Angle E6(int32_t e6); static constexpr S1Angle E7(int32_t e7); // Convenience functions -- to use when args have been fixed32s in protos. // // The arguments are static_cast into int32_t, so very large unsigned values // are treated as negative numbers. static constexpr S1Angle UnsignedE6(uint32_t e6); static constexpr S1Angle UnsignedE7(uint32_t e7); // The default constructor yields a zero angle. This is useful for STL // containers and class methods with output arguments. constexpr S1Angle() : radians_(0) {} // Return an angle larger than any finite angle. static constexpr S1Angle Infinity(); // A explicit shorthand for the default constructor. static constexpr S1Angle Zero(); // Return the angle between two points, which is also equal to the distance // between these points on the unit sphere. The points do not need to be // normalized. S1Angle(S2Point const& x, S2Point const& y); // Like the constructor above, but return the angle (i.e., distance) // between two S2LatLng points. The result has a maximum error of // 3.25 * DBL_EPSILON (or 2.5 * DBL_EPSILON for angles up to 1 radian). S1Angle(S2LatLng const& x, S2LatLng const& y); constexpr double radians() const; constexpr double degrees() const; int32_t e5() const; int32_t e6() const; int32_t e7() const; // Return the absolute value of an angle. S1Angle abs() const; // Comparison operators. friend bool operator==(S1Angle x, S1Angle y); friend bool operator!=(S1Angle x, S1Angle y); friend bool operator<(S1Angle x, S1Angle y); friend bool operator>(S1Angle x, S1Angle y); friend bool operator<=(S1Angle x, S1Angle y); friend bool operator>=(S1Angle x, S1Angle y); // Simple arithmetic operators for manipulating S1Angles. friend S1Angle operator-(S1Angle a); friend S1Angle operator+(S1Angle a, S1Angle b); friend S1Angle operator-(S1Angle a, S1Angle b); friend S1Angle operator*(double m, S1Angle a); friend S1Angle operator*(S1Angle a, double m); friend S1Angle operator/(S1Angle a, double m); friend double operator/(S1Angle a, S1Angle b); S1Angle& operator+=(S1Angle a); S1Angle& operator-=(S1Angle a); S1Angle& operator*=(double m); S1Angle& operator/=(double m); // Trigonmetric functions (not necessary but slightly more convenient). friend double sin(S1Angle a); friend double cos(S1Angle a); friend double tan(S1Angle a); // Return the angle normalized to the range (-180, 180] degrees. S1Angle Normalized() const; // Normalize this angle to the range (-180, 180] degrees. void Normalize(); // When S1Angle is used as a key in one of the btree container types // (util/btree), indicate that linear rather than binary search should be // used. This is much faster when the comparison function is cheap. typedef std::true_type goog_btree_prefer_linear_node_search; private: explicit constexpr S1Angle(double radians) : radians_(radians) {} double radians_; }; ////////////////// Implementation details follow //////////////////// inline constexpr S1Angle S1Angle::Infinity() { return S1Angle(std::numeric_limits::infinity()); } inline constexpr S1Angle S1Angle::Zero() { return S1Angle(0); } inline constexpr double S1Angle::radians() const { return radians_; } inline constexpr double S1Angle::degrees() const { return (180 / M_PI) * radians_; } // Note that the E5, E6, and E7 conversion involve two multiplications rather // than one. This is mainly for backwards compatibility (changing this would // break many tests), but it does have the nice side effect that conversions // between Degrees, E6, and E7 are exact when the arguments are integers. inline int32_t S1Angle::e5() const { return MathUtil::FastIntRound(1e5 * degrees()); } inline int32_t S1Angle::e6() const { return MathUtil::FastIntRound(1e6 * degrees()); } inline int32_t S1Angle::e7() const { return MathUtil::FastIntRound(1e7 * degrees()); } inline S1Angle S1Angle::abs() const { return S1Angle(std::fabs(radians_)); } inline bool operator==(S1Angle x, S1Angle y) { return x.radians() == y.radians(); } inline bool operator!=(S1Angle x, S1Angle y) { return x.radians() != y.radians(); } inline bool operator<(S1Angle x, S1Angle y) { return x.radians() < y.radians(); } inline bool operator>(S1Angle x, S1Angle y) { return x.radians() > y.radians(); } inline bool operator<=(S1Angle x, S1Angle y) { return x.radians() <= y.radians(); } inline bool operator>=(S1Angle x, S1Angle y) { return x.radians() >= y.radians(); } inline S1Angle operator-(S1Angle a) { return S1Angle::Radians(-a.radians()); } inline S1Angle operator+(S1Angle a, S1Angle b) { return S1Angle::Radians(a.radians() + b.radians()); } inline S1Angle operator-(S1Angle a, S1Angle b) { return S1Angle::Radians(a.radians() - b.radians()); } inline S1Angle operator*(double m, S1Angle a) { return S1Angle::Radians(m * a.radians()); } inline S1Angle operator*(S1Angle a, double m) { return S1Angle::Radians(m * a.radians()); } inline S1Angle operator/(S1Angle a, double m) { return S1Angle::Radians(a.radians() / m); } inline double operator/(S1Angle a, S1Angle b) { return a.radians() / b.radians(); } inline S1Angle& S1Angle::operator+=(S1Angle a) { radians_ += a.radians(); return *this; } inline S1Angle& S1Angle::operator-=(S1Angle a) { radians_ -= a.radians(); return *this; } inline S1Angle& S1Angle::operator*=(double m) { radians_ *= m; return *this; } inline S1Angle& S1Angle::operator/=(double m) { radians_ /= m; return *this; } inline double sin(S1Angle a) { return sin(a.radians()); } inline double cos(S1Angle a) { return cos(a.radians()); } inline double tan(S1Angle a) { return tan(a.radians()); } inline constexpr S1Angle S1Angle::Radians(double radians) { return S1Angle(radians); } inline constexpr S1Angle S1Angle::Degrees(double degrees) { return S1Angle((M_PI / 180) * degrees); } inline constexpr S1Angle S1Angle::E5(int32_t e5) { return Degrees(1e-5 * e5); } inline constexpr S1Angle S1Angle::E6(int32_t e6) { return Degrees(1e-6 * e6); } inline constexpr S1Angle S1Angle::E7(int32_t e7) { return Degrees(1e-7 * e7); } inline constexpr S1Angle S1Angle::UnsignedE6(uint32_t e6) { return E6(static_cast(e6)); } inline constexpr S1Angle S1Angle::UnsignedE7(uint32_t e7) { return E7(static_cast(e7)); } // Writes the angle in degrees with 7 digits of precision after the // decimal point, e.g. "17.3745904". std::ostream& operator<<(std::ostream& os, S1Angle a); #endif // S2_S1ANGLE_H_