/src/s2geometry/src/s2/r1interval.h
Line | Count | Source |
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_R1INTERVAL_H_ |
19 | | #define S2_R1INTERVAL_H_ |
20 | | |
21 | | #include <algorithm> |
22 | | #include <cmath> |
23 | | #include <iosfwd> |
24 | | #include <iostream> |
25 | | #include <ostream> |
26 | | |
27 | | #include "absl/hash/hash.h" |
28 | | #include "absl/log/absl_check.h" |
29 | | #include "s2/_fp_contract_off.h" // IWYU pragma: keep |
30 | | #include "s2/util/math/vector.h" // IWYU pragma: export |
31 | | |
32 | | // An R1Interval represents a closed, bounded interval on the real line. |
33 | | // It is capable of representing the empty interval (containing no points) |
34 | | // and zero-length intervals (containing a single point). |
35 | | // |
36 | | // This class is intended to be copied by value as desired. It uses |
37 | | // the default copy constructor and assignment operator. |
38 | | class R1Interval { |
39 | | public: |
40 | | // Constructor. If lo > hi, the interval is empty. |
41 | 0 | R1Interval(double lo, double hi) : bounds_(lo, hi) {} |
42 | | |
43 | | // The default constructor creates an empty interval. (Any interval where |
44 | | // lo > hi is considered to be empty.) |
45 | | // |
46 | | // Note: Don't construct an interval using the default constructor and |
47 | | // set_lo()/set_hi(), since this technique doesn't work with S1Interval and |
48 | | // is bad programming style anyways. If you need to set both endpoints, use |
49 | | // the constructor above: |
50 | | // |
51 | | // lat_bounds_ = R1Interval(lat_lo, lat_hi); |
52 | 0 | R1Interval() : bounds_(1, 0) {} |
53 | | |
54 | | // Returns an empty interval. |
55 | 0 | static R1Interval Empty() { return R1Interval(); } |
56 | | |
57 | | // Convenience method to construct an interval containing a single point. |
58 | 0 | static R1Interval FromPoint(double p) { return R1Interval(p, p); } |
59 | | |
60 | | // Convenience method to construct the minimal interval containing |
61 | | // the two given points. This is equivalent to starting with an empty |
62 | | // interval and calling AddPoint() twice, but it is more efficient. |
63 | 0 | static R1Interval FromPointPair(double p1, double p2) { |
64 | 0 | if (p1 <= p2) { |
65 | 0 | return R1Interval(p1, p2); |
66 | 0 | } else { |
67 | 0 | return R1Interval(p2, p1); |
68 | 0 | } |
69 | 0 | } |
70 | | |
71 | | // The low bound of the interval. |
72 | 0 | double lo() const { return bounds_[0]; } |
73 | | // The high bound of the interval. |
74 | 0 | double hi() const { return bounds_[1]; } |
75 | | |
76 | | // Methods to modify one endpoint of an existing R1Interval. Do not use |
77 | | // these methods if you want to replace both endpoints of the interval; use |
78 | | // a constructor instead. For example: |
79 | | // |
80 | | // *lat_bounds = R1Interval(lat_lo, lat_hi); |
81 | 0 | void set_lo(double p) { bounds_[0] = p; } |
82 | 0 | void set_hi(double p) { bounds_[1] = p; } |
83 | | |
84 | | // Methods that allow the R1Interval to be accessed as a vector. (The |
85 | | // recommended style is to use lo() and hi() whenever possible, but these |
86 | | // methods are useful when the endpoint to be selected is not constant.) |
87 | 0 | double operator[](int i) const { return bounds_[i]; } |
88 | 0 | double& operator[](int i) { return bounds_[i]; } |
89 | 0 | const Vector2_d& bounds() const { return bounds_; } |
90 | 0 | Vector2_d* mutable_bounds() { return &bounds_; } |
91 | | |
92 | | // Return true if the interval is empty, i.e. it contains no points. |
93 | 0 | bool is_empty() const { return lo() > hi(); } |
94 | | |
95 | | // Return the center of the interval. For empty intervals, |
96 | | // the result is arbitrary. |
97 | 0 | double GetCenter() const { return 0.5 * (lo() + hi()); } |
98 | | |
99 | | // Return the length of the interval. The length of an empty interval |
100 | | // is negative. |
101 | 0 | double GetLength() const { return hi() - lo(); } |
102 | | |
103 | | // Returns true if the given point is in the closed interval [lo, hi]. |
104 | 0 | bool Contains(double p) const { |
105 | 0 | return p >= lo() && p <= hi(); |
106 | 0 | } |
107 | | |
108 | | // Returns true if the given point is in the open interval (lo, hi). |
109 | 0 | bool InteriorContains(double p) const { |
110 | 0 | return p > lo() && p < hi(); |
111 | 0 | } |
112 | | |
113 | | // Return true if this interval contains the interval 'y'. |
114 | 0 | bool Contains(const R1Interval& y) const { |
115 | 0 | if (y.is_empty()) return true; |
116 | 0 | return y.lo() >= lo() && y.hi() <= hi(); |
117 | 0 | } |
118 | | |
119 | | // Return true if the interior of this interval contains the entire |
120 | | // interval 'y' (including its boundary). |
121 | 0 | bool InteriorContains(const R1Interval& y) const { |
122 | 0 | if (y.is_empty()) return true; |
123 | 0 | return y.lo() > lo() && y.hi() < hi(); |
124 | 0 | } |
125 | | |
126 | | // Return true if this interval intersects the given interval, |
127 | | // i.e. if they have any points in common. |
128 | 0 | bool Intersects(const R1Interval& y) const { |
129 | 0 | if (lo() <= y.lo()) { |
130 | 0 | return y.lo() <= hi() && y.lo() <= y.hi(); |
131 | 0 | } else { |
132 | 0 | return lo() <= y.hi() && lo() <= hi(); |
133 | 0 | } |
134 | 0 | } |
135 | | |
136 | | // Return true if the interior of this interval intersects |
137 | | // any point of the given interval (including its boundary). |
138 | 0 | bool InteriorIntersects(const R1Interval& y) const { |
139 | 0 | return y.lo() < hi() && lo() < y.hi() && lo() < hi() && y.lo() <= y.hi(); |
140 | 0 | } |
141 | | |
142 | | // Return the Hausdorff distance to the given interval 'y'. For two |
143 | | // R1Intervals x and y, this distance is defined as |
144 | | // h(x, y) = max_{p in x} min_{q in y} d(p, q). |
145 | 0 | double GetDirectedHausdorffDistance(const R1Interval& y) const { |
146 | 0 | if (is_empty()) return 0.0; |
147 | 0 | if (y.is_empty()) return HUGE_VAL; |
148 | 0 | return std::max(0.0, std::max(hi() - y.hi(), y.lo() - lo())); |
149 | 0 | } |
150 | | |
151 | | // Expand the interval so that it contains the given point "p". |
152 | 0 | void AddPoint(double p) { |
153 | 0 | if (is_empty()) { |
154 | 0 | set_lo(p); |
155 | 0 | set_hi(p); |
156 | 0 | } else if (p < lo()) { |
157 | 0 | set_lo(p); |
158 | 0 | } else if (p > hi()) { |
159 | 0 | set_hi(p); |
160 | 0 | } |
161 | 0 | } |
162 | | |
163 | | // Expand the interval so that it contains the given interval "y". |
164 | 0 | void AddInterval(const R1Interval& y) { |
165 | 0 | if (y.is_empty()) return; |
166 | 0 | if (is_empty()) { *this = y; return; } |
167 | 0 | if (y.lo() < lo()) set_lo(y.lo()); |
168 | 0 | if (y.hi() > hi()) set_hi(y.hi()); |
169 | 0 | } |
170 | | |
171 | | // Return the closest point in the interval to the given point "p". |
172 | | // The interval must be non-empty. |
173 | 0 | double Project(double p) const { |
174 | 0 | ABSL_DCHECK(!is_empty()); |
175 | 0 | return std::clamp(p, lo(), hi()); |
176 | 0 | } |
177 | | |
178 | | // Return an interval that has been expanded on each side by the given |
179 | | // distance "margin". If "margin" is negative, then shrink the interval on |
180 | | // each side by "margin" instead. The resulting interval may be empty. Any |
181 | | // expansion of an empty interval remains empty. |
182 | 0 | R1Interval Expanded(double margin) const { |
183 | 0 | if (is_empty()) return *this; |
184 | 0 | return R1Interval(lo() - margin, hi() + margin); |
185 | 0 | } |
186 | | |
187 | | // Return the smallest interval that contains this interval and the |
188 | | // given interval "y". |
189 | 0 | R1Interval Union(const R1Interval& y) const { |
190 | 0 | if (is_empty()) return y; |
191 | 0 | if (y.is_empty()) return *this; |
192 | 0 | return R1Interval(std::min(lo(), y.lo()), std::max(hi(), y.hi())); |
193 | 0 | } |
194 | | |
195 | | // Return the intersection of this interval with the given interval. |
196 | | // Empty intervals do not need to be special-cased. |
197 | 0 | R1Interval Intersection(const R1Interval& y) const { |
198 | 0 | return R1Interval(std::max(lo(), y.lo()), std::min(hi(), y.hi())); |
199 | 0 | } |
200 | | |
201 | | // Return true if two intervals contain the same set of points. |
202 | 0 | bool operator==(const R1Interval& y) const { |
203 | 0 | return (lo() == y.lo() && hi() == y.hi()) || (is_empty() && y.is_empty()); |
204 | 0 | } |
205 | | |
206 | | // Return true if two intervals do not contain the same set of points. |
207 | 0 | bool operator!=(const R1Interval& y) const { |
208 | 0 | return !operator==(y); |
209 | 0 | } |
210 | | |
211 | | // Return true if this interval can be transformed into the given interval |
212 | | // by moving each endpoint by at most "max_error". The empty interval is |
213 | | // considered to be positioned arbitrarily on the real line, thus any |
214 | | // interval with (length <= 2*max_error) matches the empty interval. |
215 | 0 | bool ApproxEquals(const R1Interval& y, double max_error = 1e-15) const { |
216 | 0 | if (is_empty()) return y.GetLength() <= 2 * max_error; |
217 | 0 | if (y.is_empty()) return GetLength() <= 2 * max_error; |
218 | 0 | return (std::fabs(y.lo() - lo()) <= max_error && |
219 | 0 | std::fabs(y.hi() - hi()) <= max_error); |
220 | 0 | } |
221 | | |
222 | | private: |
223 | | Vector2_d bounds_; |
224 | | }; |
225 | | |
226 | 0 | inline std::ostream& operator<<(std::ostream& os, const R1Interval& x) { |
227 | 0 | return os << "[" << x.lo() << ", " << x.hi() << "]"; |
228 | 0 | } |
229 | | |
230 | | template <typename H> |
231 | | H AbslHashValue(H h, const R1Interval& interval) { |
232 | | return H::combine(std::move(h), interval.lo(), interval.hi()); |
233 | | } |
234 | | |
235 | | #endif // S2_R1INTERVAL_H_ |