Coverage Report

Created: 2026-06-09 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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_