/src/s2geometry/src/s2/internal/s2index_cell_data.cc
Line | Count | Source |
1 | | // Copyright 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 | | #include "s2/internal/s2index_cell_data.h" |
17 | | |
18 | | #include <algorithm> |
19 | | #include <atomic> |
20 | | #include <utility> |
21 | | |
22 | | #include "absl/log/absl_check.h" |
23 | | #include "s2/mutable_s2shape_index.h" |
24 | | #include "s2/s2cell.h" |
25 | | #include "s2/s2padded_cell.h" |
26 | | #include "s2/s2shape.h" |
27 | | |
28 | | namespace internal { |
29 | | |
30 | | void S2IndexCellData::LoadCell(const S2ShapeIndex* index, S2CellId id, |
31 | 0 | const S2ShapeIndexCell* cell) { |
32 | 0 | ABSL_DCHECK_NE(index, nullptr); |
33 | |
|
34 | 0 | if (index_ == index && cell_id_ == id) { |
35 | 0 | return; |
36 | 0 | } |
37 | | |
38 | 0 | index_ = index; |
39 | | |
40 | | // Cache cell information. |
41 | 0 | cell_ = cell; |
42 | 0 | cell_id_ = id; |
43 | | |
44 | | // Reset atomic flags so we'll recompute cached values. These form a write |
45 | | // barrier with the write to cell_id_ above and so should stay below it. |
46 | 0 | s2cell_set_.store(false, std::memory_order_release); |
47 | 0 | center_set_.store(false, std::memory_order_release); |
48 | | |
49 | | // Clear previous edges. Reserve one slot in case we don't decode _any_ edges |
50 | | // (e.g. an interior cell or all the dimensions are unwanted, so we don't |
51 | | // try to create spans with a nullptr). |
52 | 0 | edges_.clear(); |
53 | 0 | shape_regions_.clear(); |
54 | | |
55 | | // Reset per-dimension region information. |
56 | 0 | for (auto& region : dim_regions_) { |
57 | 0 | region = Region(); |
58 | 0 | } |
59 | |
|
60 | 0 | int min_dim = 0; |
61 | 0 | while (min_dim <= 2 && !dim_wanted(min_dim)) { |
62 | 0 | ++min_dim; |
63 | 0 | } |
64 | |
|
65 | 0 | int max_dim = 2; |
66 | 0 | while (max_dim >= 0 && !dim_wanted(max_dim)) { |
67 | 0 | --max_dim; |
68 | 0 | } |
69 | | |
70 | | // No dimensions wanted, we're done. |
71 | 0 | if (min_dim > 2 || max_dim < 0) { |
72 | 0 | return; |
73 | 0 | } |
74 | | |
75 | 0 | for (int dim = min_dim; dim <= max_dim; ++dim) { |
76 | 0 | int dim_start = edges_.size(); |
77 | |
|
78 | 0 | for (const S2ClippedShape& clipped : cell_->clipped_shapes()) { |
79 | 0 | int shape_id = clipped.shape_id(); |
80 | 0 | const S2Shape& shape = *index->shape(shape_id); |
81 | | |
82 | | // Only process the current dimension. |
83 | 0 | if (shape.dimension() != dim) { |
84 | 0 | continue; |
85 | 0 | } |
86 | | |
87 | | // In the event we wanted dimensions 0 and 2, but not 1. |
88 | 0 | if (!dim_wanted(dim)) { |
89 | 0 | continue; |
90 | 0 | } |
91 | | |
92 | | // Materialize clipped shape edges into the edges vector. Track where we |
93 | | // start so we can add information about the region for this shape. |
94 | 0 | int shape_start = edges_.size(); |
95 | 0 | for (int i = 0; i < clipped.num_edges(); ++i) { |
96 | 0 | int edge_id = clipped.edge(i); |
97 | | |
98 | | // Looking up an edge requires looking up which chain it's in, which is |
99 | | // often a binary search. So let's manually lookup the chain |
100 | | // information and use that to find the edge, so we only have to do that |
101 | | // search once. |
102 | 0 | S2Shape::ChainPosition position = shape.chain_position(edge_id); |
103 | 0 | S2Shape::Edge edge = |
104 | 0 | shape.chain_edge(position.chain_id, position.offset); |
105 | 0 | edges_.emplace_back(edge, edge_id, position); |
106 | 0 | } |
107 | | |
108 | | // Note which block of edges belongs to the shape. |
109 | 0 | shape_regions_.emplace_back(std::make_pair( |
110 | 0 | shape_id, Region{shape_start, edges_.size() - shape_start})); |
111 | 0 | } |
112 | | |
113 | | // Save region information for the current dimension. |
114 | 0 | dim_regions_[dim] = {dim_start, edges_.size() - dim_start}; |
115 | 0 | } |
116 | 0 | } |
117 | | |
118 | | absl::Span<const S2IndexCellData::EdgeAndIdChain> S2IndexCellData::shape_edges( |
119 | 0 | int id) const { |
120 | 0 | for (const auto& iter : shape_regions_) { |
121 | 0 | if (iter.first == id) { |
122 | 0 | Region region = iter.second; |
123 | 0 | if (static_cast<size_t>(region.start) < edges_.size()) { |
124 | 0 | return {&edges_[region.start], region.size}; |
125 | 0 | } |
126 | 0 | return {}; |
127 | 0 | } |
128 | 0 | } |
129 | 0 | return {}; |
130 | 0 | } |
131 | | |
132 | | absl::Span<const S2IndexCellData::EdgeAndIdChain> S2IndexCellData::dim_edges( |
133 | 0 | int dim) const { |
134 | 0 | ABSL_DCHECK(0 <= dim && dim <= 2); |
135 | 0 | const Region& region = dim_regions_[dim]; |
136 | 0 | if (static_cast<size_t>(region.start) < edges_.size()) { |
137 | 0 | return {&edges_[region.start], region.size}; |
138 | 0 | } |
139 | 0 | return {}; |
140 | 0 | } |
141 | | |
142 | | absl::Span<const S2IndexCellData::EdgeAndIdChain> |
143 | 0 | S2IndexCellData::dim_range_edges(int dim0, int dim1) const { |
144 | 0 | ABSL_DCHECK_LE(dim0, dim1); |
145 | 0 | ABSL_DCHECK(0 <= dim0 && dim0 <= 2); |
146 | 0 | ABSL_DCHECK(0 <= dim1 && dim1 <= 2); |
147 | |
|
148 | 0 | size_t start = dim_regions_[dim0].start; |
149 | 0 | size_t size = 0; |
150 | 0 | for (int dim = dim0; dim <= dim1; ++dim) { |
151 | 0 | start = std::min(start, static_cast<size_t>(dim_regions_[dim].start)); |
152 | 0 | size += dim_regions_[dim].size; |
153 | 0 | } |
154 | |
|
155 | 0 | if (start < edges_.size()) { |
156 | 0 | return {&edges_[start], size}; |
157 | 0 | } |
158 | 0 | return {}; |
159 | 0 | } |
160 | | |
161 | | bool S2IndexCellData::ShapeContains(const S2ClippedShape& clipped, |
162 | | const S2Point& point, |
163 | 0 | S2VertexModel model) const { |
164 | 0 | #ifndef NDEBUG |
165 | 0 | R2Rect bounds = S2PaddedCell(id(), MutableS2ShapeIndex::kCellPadding).bound(); |
166 | |
|
167 | 0 | R2Point uv; |
168 | 0 | S2::ValidFaceXYZtoUV(S2Cell(id()).face(), point, &uv); |
169 | 0 | ABSL_DCHECK(bounds.Contains(uv)); |
170 | 0 | #endif |
171 | | |
172 | | // Points and polylines don't contain anything except under the CLOSED model. |
173 | 0 | const S2Shape& shape = *index_->shape(clipped.shape_id()); |
174 | 0 | if (shape.dimension() < 2) { |
175 | 0 | if (model != S2VertexModel::CLOSED) { |
176 | 0 | return false; |
177 | 0 | } |
178 | | |
179 | | // Point/polyline only contains point if it's a vertex. |
180 | 0 | for (const EdgeAndIdChain& edge : shape_edges(clipped.shape_id())) { |
181 | 0 | if (edge.v0 == point || edge.v1 == point) { |
182 | 0 | return true; |
183 | 0 | } |
184 | 0 | } |
185 | 0 | return false; |
186 | 0 | } |
187 | | |
188 | | // Test containment by drawing a line segment from the cell center to the |
189 | | // given point and counting edge crossings. |
190 | 0 | S2Point center = this->center(); |
191 | 0 | S2EdgeCrosser crosser(¢er, &point); |
192 | |
|
193 | 0 | bool inside = clipped.contains_center(); |
194 | 0 | for (const EdgeAndIdChain& edge : shape_edges(clipped.shape_id())) { |
195 | 0 | int sign = crosser.CrossingSign(&edge.v0, &edge.v1); |
196 | 0 | if (sign < 0) continue; |
197 | 0 | if (sign == 0) { |
198 | | // For the OPEN and CLOSED models, check whether point is a vertex. |
199 | 0 | if (model != S2VertexModel::SEMI_OPEN && |
200 | 0 | (edge.v0 == point || edge.v1 == point)) { |
201 | 0 | return (model == S2VertexModel::CLOSED); |
202 | 0 | } |
203 | 0 | sign = S2::VertexCrossing(*crosser.a(), *crosser.b(), edge.v0, edge.v1); |
204 | 0 | } |
205 | 0 | inside ^= sign; |
206 | 0 | } |
207 | 0 | return inside; |
208 | 0 | } |
209 | | |
210 | | } // namespace internal |