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