/src/s2geometry/src/s2/s2padded_cell.cc
Line | Count | Source |
1 | | // Copyright 2013 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 | | #include "s2/s2padded_cell.h" |
19 | | |
20 | | #include <algorithm> |
21 | | #include <cfloat> |
22 | | |
23 | | #include "absl/base/optimization.h" |
24 | | #include "absl/log/absl_check.h" |
25 | | #include "absl/numeric/bits.h" |
26 | | #include "s2/r1interval.h" |
27 | | #include "s2/r2rect.h" |
28 | | #include "s2/s2cell_id.h" |
29 | | #include "s2/s2coords.h" |
30 | | #include "s2/s2coords_internal.h" |
31 | | #include "s2/s2point.h" |
32 | | |
33 | | using std::max; |
34 | | using std::min; |
35 | | using S2::internal::kSwapMask; |
36 | | using S2::internal::kInvertMask; |
37 | | using S2::internal::kIJtoPos; |
38 | | using S2::internal::kPosToOrientation; |
39 | | |
40 | | S2PaddedCell::S2PaddedCell(S2CellId id, double padding) |
41 | 0 | : id_(id), padding_(padding) { |
42 | 0 | if (id_.is_face()) { |
43 | | // Fast path for constructing a top-level face (the most common case). |
44 | 0 | double limit = 1 + padding; |
45 | 0 | bound_ = R2Rect(R1Interval(-limit, limit), R1Interval(-limit, limit)); |
46 | 0 | middle_ = R2Rect(R1Interval(-padding, padding), |
47 | 0 | R1Interval(-padding, padding)); |
48 | 0 | ij_lo_[0] = ij_lo_[1] = 0; |
49 | 0 | orientation_ = id_.face() & 1; |
50 | 0 | level_ = 0; |
51 | 0 | } else { |
52 | 0 | int ij[2]; |
53 | 0 | id.ToFaceIJOrientation(&ij[0], &ij[1], &orientation_); |
54 | 0 | level_ = id.level(); |
55 | 0 | bound_ = S2CellId::IJLevelToBoundUV(ij, level_).Expanded(padding); |
56 | 0 | int ij_size = S2CellId::GetSizeIJ(level_); |
57 | 0 | ij_lo_[0] = ij[0] & -ij_size; |
58 | 0 | ij_lo_[1] = ij[1] & -ij_size; |
59 | 0 | } |
60 | 0 | } |
61 | | |
62 | | S2PaddedCell::S2PaddedCell(const S2PaddedCell& parent, int i, int j) |
63 | 0 | : padding_(parent.padding_), |
64 | 0 | bound_(parent.bound_), |
65 | 0 | level_(parent.level_ + 1) { |
66 | | // Compute the position and orientation of the child incrementally from the |
67 | | // orientation of the parent. |
68 | 0 | int pos = kIJtoPos[parent.orientation_][2*i+j]; |
69 | 0 | id_ = parent.id_.child(pos); |
70 | 0 | int ij_size = S2CellId::GetSizeIJ(level_); |
71 | 0 | ij_lo_[0] = parent.ij_lo_[0] + i * ij_size; |
72 | 0 | ij_lo_[1] = parent.ij_lo_[1] + j * ij_size; |
73 | 0 | orientation_ = parent.orientation_ ^ kPosToOrientation[pos]; |
74 | | // For each child, one corner of the bound is taken directly from the parent |
75 | | // while the diagonally opposite corner is taken from middle(). |
76 | 0 | const R2Rect& middle = parent.middle(); |
77 | 0 | bound_[0][1-i] = middle[0][1-i]; |
78 | 0 | bound_[1][1-j] = middle[1][1-j]; |
79 | 0 | } |
80 | | |
81 | 0 | const R2Rect& S2PaddedCell::middle() const { |
82 | | // We compute this field lazily because it is not needed the majority of the |
83 | | // time (i.e., for cells where the recursion terminates). |
84 | 0 | if (middle_.is_empty()) { |
85 | 0 | int ij_size = S2CellId::GetSizeIJ(level_); |
86 | 0 | double u = S2::STtoUV(S2::SiTitoST(2 * ij_lo_[0] + ij_size)); |
87 | 0 | double v = S2::STtoUV(S2::SiTitoST(2 * ij_lo_[1] + ij_size)); |
88 | 0 | middle_ = R2Rect(R1Interval(u - padding_, u + padding_), |
89 | 0 | R1Interval(v - padding_, v + padding_)); |
90 | 0 | } |
91 | 0 | return middle_; |
92 | 0 | } |
93 | | |
94 | 0 | S2Point S2PaddedCell::GetCenter() const { |
95 | 0 | int ij_size = S2CellId::GetSizeIJ(level_); |
96 | 0 | unsigned int si = 2 * ij_lo_[0] + ij_size; |
97 | 0 | unsigned int ti = 2 * ij_lo_[1] + ij_size; |
98 | 0 | return S2::FaceSiTitoXYZ(id_.face(), si, ti).Normalize(); |
99 | 0 | } |
100 | | |
101 | 0 | S2Point S2PaddedCell::GetEntryVertex() const { |
102 | | // The curve enters at the (0,0) vertex unless the axis directions are |
103 | | // reversed, in which case it enters at the (1,1) vertex. |
104 | 0 | unsigned int i = ij_lo_[0]; |
105 | 0 | unsigned int j = ij_lo_[1]; |
106 | 0 | if (orientation_ & kInvertMask) { |
107 | 0 | int ij_size = S2CellId::GetSizeIJ(level_); |
108 | 0 | i += ij_size; |
109 | 0 | j += ij_size; |
110 | 0 | } |
111 | 0 | return S2::FaceSiTitoXYZ(id_.face(), 2 * i, 2 * j).Normalize(); |
112 | 0 | } |
113 | | |
114 | 0 | S2Point S2PaddedCell::GetExitVertex() const { |
115 | | // The curve exits at the (1,0) vertex unless the axes are swapped or |
116 | | // inverted but not both, in which case it exits at the (0,1) vertex. |
117 | 0 | unsigned int i = ij_lo_[0]; |
118 | 0 | unsigned int j = ij_lo_[1]; |
119 | 0 | int ij_size = S2CellId::GetSizeIJ(level_); |
120 | 0 | if (orientation_ == 0 || orientation_ == kSwapMask + kInvertMask) { |
121 | 0 | i += ij_size; |
122 | 0 | } else { |
123 | 0 | j += ij_size; |
124 | 0 | } |
125 | 0 | return S2::FaceSiTitoXYZ(id_.face(), 2 * i, 2 * j).Normalize(); |
126 | 0 | } |
127 | | |
128 | 0 | S2CellId S2PaddedCell::ShrinkToFit(const R2Rect& rect) const { |
129 | 0 | ABSL_DCHECK(bound().Intersects(rect)); |
130 | | |
131 | | // Quick rejection test: if "rect" contains the center of this cell along |
132 | | // either axis, then no further shrinking is possible. |
133 | 0 | int ij_size = S2CellId::GetSizeIJ(level_); |
134 | 0 | if (level_ == 0) { |
135 | | // Fast path (most calls to this function start with a face cell). |
136 | 0 | if (rect[0].Contains(0) || rect[1].Contains(0)) return id(); |
137 | 0 | } else { |
138 | 0 | if (rect[0].Contains(S2::STtoUV(S2::SiTitoST(2 * ij_lo_[0] + ij_size))) || |
139 | 0 | rect[1].Contains(S2::STtoUV(S2::SiTitoST(2 * ij_lo_[1] + ij_size)))) { |
140 | 0 | return id(); |
141 | 0 | } |
142 | 0 | } |
143 | | // Otherwise we expand "rect" by the given padding() on all sides and find |
144 | | // the range of coordinates that it spans along the i- and j-axes. We then |
145 | | // compute the highest bit position at which the min and max coordinates |
146 | | // differ. This corresponds to the first cell level at which at least two |
147 | | // children intersect "rect". |
148 | | |
149 | | // Increase the padding to compensate for the error in S2::UVtoST(). |
150 | | // (The constant below is a provable upper bound on the additional error.) |
151 | 0 | R2Rect padded = rect.Expanded(padding() + 1.5 * DBL_EPSILON); |
152 | 0 | int ij_min[2]; // Min i- or j- coordinate spanned by "padded" |
153 | 0 | int ij_xor[2]; // XOR of the min and max i- or j-coordinates |
154 | 0 | for (int d = 0; d < 2; ++d) { |
155 | 0 | ij_min[d] = max(ij_lo_[d], S2::STtoIJ(S2::UVtoST(padded[d][0]))); |
156 | 0 | int ij_max = min(ij_lo_[d] + ij_size - 1, |
157 | 0 | S2::STtoIJ(S2::UVtoST(padded[d][1]))); |
158 | 0 | ij_xor[d] = ij_min[d] ^ ij_max; |
159 | 0 | } |
160 | | // Compute the highest bit position where the two i- or j-endpoints differ, |
161 | | // and then choose the cell level that includes both of these endpoints. So |
162 | | // if both pairs of endpoints are equal we choose kMaxLevel; if they differ |
163 | | // only at bit 0, we choose (kMaxLevel - 1), and so on. |
164 | 0 | unsigned int level_msb = ((ij_xor[0] | ij_xor[1]) << 1) + 1; |
165 | 0 | ABSL_ASSUME(level_msb != 0); |
166 | 0 | int level = S2CellId::kMaxLevel - (absl::bit_width(level_msb) - 1); |
167 | 0 | if (level <= level_) return id(); |
168 | 0 | return S2CellId::FromFaceIJ(id().face(), ij_min[0], ij_min[1]).parent(level); |
169 | 0 | } |