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 <math.h>
#include <limits>
#include <ostream>
#include <type_traits>
#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<double>::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<int32_t>(e6));
}
inline constexpr S1Angle S1Angle::UnsignedE7(uint32_t e7) {
return E7(static_cast<int32_t>(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_