1 // Copyright 2005 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS-IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 
16 // Author: ericv@google.com (Eric Veach)
17 
18 #ifndef S2_S1INTERVAL_H_
19 #define S2_S1INTERVAL_H_
20 
21 #include <cmath>
22 #include <iosfwd>
23 #include <iostream>
24 
25 #include "s2/base/logging.h"
26 #include "s2/_fp_contract_off.h"
27 #include "s2/util/math/vector.h"  // IWYU pragma: export
28 
29 // An S1Interval represents a closed interval on a unit circle (also known
30 // as a 1-dimensional sphere).  It is capable of representing the empty
31 // interval (containing no points), the full interval (containing all
32 // points), and zero-length intervals (containing a single point).
33 //
34 // Points are represented by the angle they make with the positive x-axis in
35 // the range [-Pi, Pi].  An interval is represented by its lower and upper
36 // bounds (both inclusive, since the interval is closed).  The lower bound may
37 // be greater than the upper bound, in which case the interval is "inverted"
38 // (i.e. it passes through the point (-1, 0)).
39 //
40 // Note that the point (-1, 0) has two valid representations, Pi and -Pi.
41 // The normalized representation of this point internally is Pi, so that
42 // endpoints of normal intervals are in the range (-Pi, Pi].  However, we
43 // take advantage of the point -Pi to construct two special intervals:
44 // the Full() interval is [-Pi, Pi], and the Empty() interval is [Pi, -Pi].
45 //
46 // This class is intended to be copied by value as desired.  It uses
47 // the default copy constructor and assignment operator.
48 class S1Interval {
49  public:
50   // Constructor.  Both endpoints must be in the range -Pi to Pi inclusive.
51   // The value -Pi is converted internally to Pi except for the Full()
52   // and Empty() intervals.
53   S1Interval(double lo, double hi);
54 
55   // The default constructor creates an empty interval.
56   //
57   // Note: Don't construct an interval using the default constructor and
58   // set_lo()/set_hi().  If you need to set both endpoints, use the
59   // constructor above:
60   //
61   //   lng_bounds_ = S1Interval(lng_lo, lng_hi);
62   S1Interval();
63 
64   // Returns the empty interval.
65   static S1Interval Empty();
66 
67   // Returns the full interval.
68   static S1Interval Full();
69 
70   // Convenience method to construct an interval containing a single point.
71   static S1Interval FromPoint(double p);
72 
73   // Convenience method to construct the minimal interval containing
74   // the two given points.  This is equivalent to starting with an empty
75   // interval and calling AddPoint() twice, but it is more efficient.
76   static S1Interval FromPointPair(double p1, double p2);
77 
78   // Accessors methods.
lo()79   double lo() const { return bounds_[0]; }
hi()80   double hi() const { return bounds_[1]; }
81 
82   // Methods that allow the S1Interval to be accessed as a vector.  (The
83   // recommended style is to use lo() and hi() whenever possible, but these
84   // methods are useful when the endpoint to be selected is not constant.)
85   //
86   // Only const versions of these methods are provided, since S1Interval
87   // has invariants that must be maintained after each update.
88   double operator[](int i) const { return bounds_[i]; }
bounds()89   const Vector2_d& bounds() const { return bounds_; }
90 
91   // An interval is valid if neither bound exceeds Pi in absolute value,
92   // and the value -Pi appears only in the Empty() and Full() intervals.
93   bool is_valid() const;
94 
95   // Return true if the interval contains all points on the unit circle.
is_full()96   bool is_full() const { return lo() == -M_PI && hi() == M_PI; }
97 
98   // Return true if the interval is empty, i.e. it contains no points.
is_empty()99   bool is_empty() const { return lo() == M_PI && hi() == -M_PI; }
100 
101   // Return true if lo() > hi().  (This is true for empty intervals.)
is_inverted()102   bool is_inverted() const { return lo() > hi(); }
103 
104   // Return the midpoint of the interval.  For full and empty intervals,
105   // the result is arbitrary.
106   double GetCenter() const;
107 
108   // Return the length of the interval.  The length of an empty interval
109   // is negative.
110   double GetLength() const;
111 
112   // Return the complement of the interior of the interval.  An interval and
113   // its complement have the same boundary but do not share any interior
114   // values.  The complement operator is not a bijection, since the complement
115   // of a singleton interval (containing a single value) is the same as the
116   // complement of an empty interval.
117   S1Interval Complement() const;
118 
119   // Return the midpoint of the complement of the interval. For full and empty
120   // intervals, the result is arbitrary. For a singleton interval (containing a
121   // single point), the result is its antipodal point on S1.
122   double GetComplementCenter() const;
123 
124   // Return true if the interval (which is closed) contains the point 'p'.
125   bool Contains(double p) const;
126 
127   // Return true if the interior of the interval contains the point 'p'.
128   bool InteriorContains(double p) const;
129 
130   // Return true if the interval contains the given interval 'y'.
131   // Works for empty, full, and singleton intervals.
132   bool Contains(const S1Interval& y) const;
133 
134   // Returns true if the interior of this interval contains the entire
135   // interval 'y'.  Note that x.InteriorContains(x) is true only when
136   // x is the empty or full interval, and x.InteriorContains(S1Interval(p,p))
137   // is equivalent to x.InteriorContains(p).
138   bool InteriorContains(const S1Interval& y) const;
139 
140   // Return true if the two intervals contain any points in common.
141   // Note that the point +/-Pi has two representations, so the intervals
142   // [-Pi,-3] and [2,Pi] intersect, for example.
143   bool Intersects(const S1Interval& y) const;
144 
145   // Return true if the interior of this interval contains any point of the
146   // interval 'y' (including its boundary).  Works for empty, full, and
147   // singleton intervals.
148   bool InteriorIntersects(const S1Interval& y) const;
149 
150   // Return the Hausdorff distance to the given interval 'y'. For two
151   // S1Intervals x and y, this distance is defined by
152   //     h(x, y) = max_{p in x} min_{q in y} d(p, q),
153   // where d(.,.) is measured along S1.
154   double GetDirectedHausdorffDistance(const S1Interval& y) const;
155 
156   // Expand the interval by the minimum amount necessary so that it
157   // contains the given point "p" (an angle in the range [-Pi, Pi]).
158   void AddPoint(double p);
159 
160   // Return the closest point in the interval to the given point "p".
161   // The interval must be non-empty.
162   double Project(double p) const;
163 
164   // Return an interval that has been expanded on each side by the given
165   // distance "margin".  If "margin" is negative, then shrink the interval on
166   // each side by "margin" instead.  The resulting interval may be empty or
167   // full.  Any expansion (positive or negative) of a full interval remains
168   // full, and any expansion of an empty interval remains empty.
169   S1Interval Expanded(double margin) const;
170 
171   // Return the smallest interval that contains this interval and the
172   // given interval "y".
173   S1Interval Union(const S1Interval& y) const;
174 
175   // Return the smallest interval that contains the intersection of this
176   // interval with "y".  Note that the region of intersection may
177   // consist of two disjoint intervals.
178   S1Interval Intersection(const S1Interval& y) const;
179 
180   // Return true if two intervals contains the same set of points.
181   bool operator==(const S1Interval& y) const;
182 
183   // Return true if this interval can be transformed into the given interval by
184   // moving each endpoint by at most "max_error" (and without the endpoints
185   // crossing, which would invert the interval).  Empty and full intervals are
186   // considered to start at an arbitrary point on the unit circle, thus any
187   // interval with (length <= 2*max_error) matches the empty interval, and any
188   // interval with (length >= 2*Pi - 2*max_error) matches the full interval.
189   bool ApproxEquals(const S1Interval& y, double max_error = 1e-15) const;
190 
191   // Low-level methods to modify one endpoint of an existing S1Interval.
192   // These methods should really be private because setting just one endpoint
193   // can violate the invariants maintained by S1Interval.  In particular:
194   //
195   //  - It is not valid to call these methods on an Empty() or Full()
196   //    interval, since these intervals do not have any endpoints.
197   //
198   //  - It is not allowed to set an endpoint to -Pi.  (When these methods are
199   //    used internally, values of -Pi have already been normalized to Pi.)
200   //
201   // The preferred way to modify both endpoints of an interval is to use a
202   // constructor, e.g. lng = S1Interval(lng_lo, lng_hi).
203   void set_lo(double p);
204   void set_hi(double p);
205 
206  private:
207   enum ArgsChecked { ARGS_CHECKED };
208 
209   // Internal constructor that assumes that both arguments are in the
210   // correct range, i.e. normalization from -Pi to Pi is already done.
211   S1Interval(double lo, double hi, ArgsChecked dummy);
212 
213   // Return true if the interval (which is closed) contains the point 'p'.
214   // Skips the normalization of 'p' from -Pi to Pi.
215   bool FastContains(double p) const;
216 
217   Vector2_d bounds_;
218 };
219 
S1Interval(double lo,double hi)220 inline S1Interval::S1Interval(double lo, double hi) : bounds_(lo, hi) {
221   if (lo == -M_PI && hi != M_PI) set_lo(M_PI);
222   if (hi == -M_PI && lo != M_PI) set_hi(M_PI);
223   S2_DCHECK(is_valid());
224 }
225 
S1Interval(double lo,double hi,ArgsChecked dummy)226 inline S1Interval::S1Interval(double lo, double hi, ArgsChecked dummy)
227   : bounds_(lo, hi) {
228   S2_DCHECK(is_valid());
229 }
230 
S1Interval()231 inline S1Interval::S1Interval() : bounds_(M_PI, -M_PI) {
232 }
233 
Empty()234 inline S1Interval S1Interval::Empty() {
235   return S1Interval();
236 }
237 
Full()238 inline S1Interval S1Interval::Full() {
239   return S1Interval(-M_PI, M_PI, ARGS_CHECKED);
240 }
241 
is_valid()242 inline bool S1Interval::is_valid() const {
243   return (std::fabs(lo()) <= M_PI && std::fabs(hi()) <= M_PI &&
244           !(lo() == -M_PI && hi() != M_PI) &&
245           !(hi() == -M_PI && lo() != M_PI));
246 }
247 
248 inline bool S1Interval::operator==(const S1Interval& y) const {
249   return lo() == y.lo() && hi() == y.hi();
250 }
251 
set_lo(double p)252 inline void S1Interval::set_lo(double p) {
253   bounds_[0] = p;
254   S2_DCHECK(is_valid());
255 }
256 
set_hi(double p)257 inline void S1Interval::set_hi(double p) {
258   bounds_[1] = p;
259   S2_DCHECK(is_valid());
260 }
261 
262 inline std::ostream& operator<<(std::ostream& os, const S1Interval& x) {
263   return os << "[" << x.lo() << ", " << x.hi() << "]";
264 }
265 
266 #endif  // S2_S1INTERVAL_H_
267