/src/s2geometry/src/s2/s2lax_polygon_shape.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/s2lax_polygon_shape.h" |
19 | | |
20 | | #include <algorithm> |
21 | | #include <atomic> |
22 | | #include <cstdint> |
23 | | #include <cstdlib> |
24 | | #include <limits> |
25 | | #include <memory> |
26 | | #include <utility> |
27 | | #include <vector> |
28 | | |
29 | | #include "absl/base/nullability.h" |
30 | | #include "absl/log/absl_check.h" |
31 | | #include "absl/memory/memory.h" |
32 | | #include "absl/strings/string_view.h" |
33 | | #include "absl/types/span.h" |
34 | | |
35 | | #include "s2/encoded_s2point_vector.h" |
36 | | #include "s2/encoded_uint_vector.h" |
37 | | #include "s2/s2coder.h" |
38 | | #include "s2/s2error.h" |
39 | | #include "s2/s2loop.h" |
40 | | #include "s2/s2point.h" |
41 | | #include "s2/s2point_array.h" |
42 | | #include "s2/s2polygon.h" |
43 | | #include "s2/s2shape.h" |
44 | | #include "s2/s2shapeutil_get_reference_point.h" |
45 | | #include "s2/util/coding/coder.h" |
46 | | #include "s2/util/coding/varint.h" |
47 | | |
48 | | using absl::MakeSpan; |
49 | | using absl::Span; |
50 | | using ::s2internal::MakeS2PointArrayForOverwrite; |
51 | | using std::unique_ptr; |
52 | | using std::vector; |
53 | | using ChainPosition = S2Shape::ChainPosition; |
54 | | |
55 | | namespace { |
56 | | template <typename T> |
57 | 3.93k | unique_ptr<T> make_unique_for_overwrite(size_t n) { |
58 | | // We only need to support this one variant. |
59 | 3.93k | static_assert(std::is_array<T>::value, "T must be an array type"); |
60 | 3.93k | return unique_ptr<T>(new typename absl::remove_extent_t<T>[n]); |
61 | 3.93k | } |
62 | | } // namespace |
63 | | |
64 | | |
65 | | // When adding a new encoding, be aware that old binaries will not be able |
66 | | // to decode it. |
67 | | static const unsigned char kCurrentEncodingVersionNumber = 1; |
68 | | |
69 | | S2LaxPolygonShape::S2LaxPolygonShape( |
70 | 14.2k | absl::Span<const S2LaxPolygonShape::Loop> loops) { |
71 | 14.2k | Init(loops); |
72 | 14.2k | } |
73 | | |
74 | 0 | S2LaxPolygonShape::S2LaxPolygonShape(Span<const Span<const S2Point>> loops) { |
75 | 0 | Init(loops); |
76 | 0 | } |
77 | | |
78 | 0 | S2LaxPolygonShape::S2LaxPolygonShape(const S2Polygon& polygon) { |
79 | 0 | Init(polygon); |
80 | 0 | } |
81 | | |
82 | | S2LaxPolygonShape::S2LaxPolygonShape(S2LaxPolygonShape&& b) noexcept |
83 | 0 | : num_loops_(std::exchange(b.num_loops_, 0)), |
84 | 0 | prev_loop_(b.prev_loop_.exchange(0, std::memory_order_relaxed)), |
85 | 0 | num_vertices_(std::exchange(b.num_vertices_, 0)), |
86 | 0 | vertices_(std::move(b.vertices_)), |
87 | 0 | loop_starts_(std::move(b.loop_starts_)) {} |
88 | | |
89 | | S2LaxPolygonShape& S2LaxPolygonShape::operator=( |
90 | 0 | S2LaxPolygonShape&& b) noexcept { |
91 | 0 | using std::memory_order_relaxed; |
92 | |
|
93 | 0 | num_loops_ = std::exchange(b.num_loops_, 0); |
94 | 0 | prev_loop_.store(b.prev_loop_.exchange(0, memory_order_relaxed), |
95 | 0 | memory_order_relaxed); |
96 | 0 | num_vertices_ = std::exchange(b.num_vertices_, 0); |
97 | 0 | vertices_ = std::move(b.vertices_); |
98 | 0 | loop_starts_ = std::move(b.loop_starts_); |
99 | 0 | return *this; |
100 | 0 | } |
101 | | |
102 | 14.2k | void S2LaxPolygonShape::Init(absl::Span<const S2LaxPolygonShape::Loop> loops) { |
103 | 14.2k | vector<Span<const S2Point>> spans; |
104 | 14.2k | spans.reserve(loops.size()); |
105 | 3.03M | for (const S2LaxPolygonShape::Loop& loop : loops) { |
106 | 3.03M | spans.emplace_back(loop); |
107 | 3.03M | } |
108 | 14.2k | Init(spans); |
109 | 14.2k | } |
110 | | |
111 | 0 | void S2LaxPolygonShape::Init(const S2Polygon& polygon) { |
112 | 0 | vector<Span<const S2Point>> spans; |
113 | 0 | for (int i = 0; i < polygon.num_loops(); ++i) { |
114 | 0 | const S2Loop* loop = polygon.loop(i); |
115 | 0 | if (loop->is_full()) { |
116 | 0 | spans.emplace_back(); // Empty span. |
117 | 0 | } else { |
118 | 0 | spans.emplace_back(&loop->vertex(0), loop->num_vertices()); |
119 | 0 | } |
120 | 0 | } |
121 | 0 | Init(spans); |
122 | | |
123 | | // S2Polygon and S2LaxPolygonShape holes are oriented oppositely, so we need |
124 | | // to reverse the orientation of any loops representing holes. |
125 | 0 | for (int i = 0; i < polygon.num_loops(); ++i) { |
126 | 0 | if (polygon.loop(i)->is_hole()) { |
127 | 0 | S2Point* v0 = &vertices_[loop_starts_[i]]; |
128 | 0 | std::reverse(v0, v0 + num_loop_vertices(i)); |
129 | 0 | } |
130 | 0 | } |
131 | 0 | } |
132 | | |
133 | 14.2k | void S2LaxPolygonShape::Init(Span<const Span<const S2Point>> loops) { |
134 | 14.2k | num_loops_ = loops.size(); |
135 | 14.2k | if (num_loops_ == 0) { |
136 | 2.63k | num_vertices_ = 0; |
137 | 11.5k | } else if (num_loops_ == 1) { |
138 | 7.63k | num_vertices_ = loops[0].size(); |
139 | 7.63k | vertices_ = MakeS2PointArrayForOverwrite(num_vertices_); |
140 | 7.63k | std::copy_n(loops[0].data(), num_vertices_, vertices_.get()); |
141 | 7.63k | } else { |
142 | | // Don't use make_unique<> here in order to avoid zero initialization. |
143 | 3.93k | loop_starts_ = make_unique_for_overwrite<uint32_t[]>(num_loops_ + 1); |
144 | 3.93k | num_vertices_ = 0; |
145 | 3.02M | for (int i = 0; i < num_loops_; ++i) { |
146 | 3.02M | loop_starts_[i] = num_vertices_; |
147 | 3.02M | num_vertices_ += loops[i].size(); |
148 | 3.02M | } |
149 | 3.93k | loop_starts_[num_loops_] = num_vertices_; |
150 | 3.93k | vertices_ = MakeS2PointArrayForOverwrite(num_vertices_); |
151 | 3.02M | for (int i = 0; i < num_loops_; ++i) { |
152 | 3.02M | std::copy_n(loops[i].data(), loops[i].size(), |
153 | 3.02M | &vertices_[loop_starts_[i]]); |
154 | 3.02M | } |
155 | 3.93k | } |
156 | 14.2k | } |
157 | | |
158 | 0 | int S2LaxPolygonShape::num_loop_vertices(int i) const { |
159 | 0 | ABSL_DCHECK_LT(i, num_loops()); |
160 | 0 | if (num_loops() == 1) { |
161 | 0 | return num_vertices_; |
162 | 0 | } else { |
163 | 0 | return loop_starts_[i + 1] - loop_starts_[i]; |
164 | 0 | } |
165 | 0 | } |
166 | | |
167 | 0 | const S2Point& S2LaxPolygonShape::loop_vertex(int i, int j) const { |
168 | 0 | ABSL_DCHECK_LT(i, num_loops()); |
169 | 0 | ABSL_DCHECK_LT(j, num_loop_vertices(i)); |
170 | 0 | if (i == 0) { |
171 | 0 | return vertices_[j]; |
172 | 0 | } else { |
173 | 0 | return vertices_[loop_starts_[i] + j]; |
174 | 0 | } |
175 | 0 | } |
176 | | |
177 | | void S2LaxPolygonShape::Encode(Encoder* encoder, |
178 | 0 | s2coding::CodingHint hint) const { |
179 | 0 | encoder->Ensure(1 + Varint::kMax32); |
180 | 0 | encoder->put8(kCurrentEncodingVersionNumber); |
181 | 0 | encoder->put_varint32(num_loops()); |
182 | 0 | s2coding::EncodeS2PointVector(MakeSpan(vertices_.get(), num_vertices()), |
183 | 0 | hint, encoder); |
184 | 0 | if (num_loops() > 1) { |
185 | 0 | s2coding::EncodeUintVector<uint32_t>( |
186 | 0 | MakeSpan(loop_starts_.get(), num_loops() + 1), encoder); |
187 | 0 | } |
188 | 0 | } |
189 | | |
190 | 0 | bool S2LaxPolygonShape::Init(Decoder* decoder, S2Error* absl_nullable error) { |
191 | 0 | const auto Error = [&error](absl::string_view message) { |
192 | 0 | if (error != nullptr) { |
193 | 0 | *error = S2Error::DataLoss(message); |
194 | 0 | } |
195 | 0 | return false; |
196 | 0 | }; |
197 | |
|
198 | 0 | if (decoder->avail() < 1) { |
199 | 0 | return Error("Insufficient data in decoder"); |
200 | 0 | } |
201 | | |
202 | 0 | uint8_t version = decoder->get8(); |
203 | 0 | if (version != kCurrentEncodingVersionNumber) { |
204 | 0 | return Error("Bad version number in byte string"); |
205 | 0 | } |
206 | | |
207 | 0 | uint32_t num_loops; |
208 | 0 | if (!decoder->get_varint32(&num_loops)) { |
209 | 0 | return Error("Failed to decode number of loops"); |
210 | 0 | } |
211 | | // `loop_starts_` is indexed by an integer, and contains an extra entry, |
212 | | // so we limit num_loops to INT32_MAX - 1. |
213 | 0 | if (num_loops > std::numeric_limits<int32_t>::max() - 1u) { |
214 | 0 | return Error("Number of loops too large"); |
215 | 0 | } |
216 | | |
217 | 0 | num_loops_ = num_loops; |
218 | 0 | s2coding::EncodedS2PointVector vertices; |
219 | 0 | if (!vertices.Init(decoder)) { |
220 | 0 | return Error("Failed to decode vertices"); |
221 | 0 | } |
222 | | |
223 | 0 | if (num_loops_ == 0) { |
224 | 0 | num_vertices_ = 0; |
225 | 0 | } else { |
226 | 0 | num_vertices_ = vertices.size(); |
227 | 0 | vertices_ = MakeS2PointArrayForOverwrite(num_vertices_); |
228 | | |
229 | | // Load the polygon vertices from the encoded s2point vector. |
230 | 0 | if (error == nullptr) { |
231 | 0 | vertices.Decode(absl::MakeSpan(vertices_.get(), num_vertices_)); |
232 | 0 | } else { |
233 | 0 | vertices.Decode(absl::MakeSpan(vertices_.get(), num_vertices_), *error); |
234 | 0 | if (!error->ok()) { |
235 | 0 | return false; |
236 | 0 | } |
237 | 0 | } |
238 | | |
239 | 0 | if (num_loops_ > 1) { |
240 | 0 | s2coding::EncodedUintVector<uint32_t> loop_starts; |
241 | 0 | if (!loop_starts.Init(decoder) || loop_starts.size() != num_loops_ + 1) { |
242 | 0 | return Error("Failed to decode loop offsets"); |
243 | 0 | } |
244 | | |
245 | 0 | loop_starts_ = make_unique_for_overwrite<uint32_t[]>(loop_starts.size()); |
246 | 0 | for (size_t i = 0; i < loop_starts.size(); ++i) { |
247 | 0 | loop_starts_[i] = loop_starts[i]; |
248 | 0 | } |
249 | 0 | } |
250 | 0 | } |
251 | | |
252 | 0 | return true; |
253 | 0 | } |
254 | | |
255 | 0 | S2Shape::Edge S2LaxPolygonShape::edge(int e) const { |
256 | | // Method names are fully specified to enable inlining. |
257 | 0 | ChainPosition pos = S2LaxPolygonShape::chain_position(e); |
258 | 0 | return S2LaxPolygonShape::chain_edge(pos.chain_id, pos.offset); |
259 | 0 | } |
260 | | |
261 | 0 | S2Shape::ReferencePoint S2LaxPolygonShape::GetReferencePoint() const { |
262 | 0 | return s2shapeutil::GetReferencePoint(*this); |
263 | 0 | } |
264 | | |
265 | 0 | S2Shape::Chain S2LaxPolygonShape::chain(int i) const { |
266 | 0 | ABSL_DCHECK_LT(i, num_loops()); |
267 | 0 | if (num_loops() == 1) { |
268 | 0 | return Chain(0, num_vertices_); |
269 | 0 | } else { |
270 | 0 | int start = loop_starts_[i]; |
271 | 0 | return Chain(start, loop_starts_[i + 1] - start); |
272 | 0 | } |
273 | 0 | } |
274 | | |
275 | | EncodedS2LaxPolygonShape::EncodedS2LaxPolygonShape( |
276 | | EncodedS2LaxPolygonShape&& b) noexcept |
277 | 0 | : num_loops_(std::exchange(b.num_loops_, 0)), |
278 | 0 | prev_loop_(b.prev_loop_.exchange(0, std::memory_order_relaxed)), |
279 | 0 | vertices_(std::move(b.vertices_)), |
280 | 0 | loop_starts_(std::move(b.loop_starts_)) {} |
281 | | |
282 | | EncodedS2LaxPolygonShape& EncodedS2LaxPolygonShape::operator=( |
283 | 0 | EncodedS2LaxPolygonShape&& b) noexcept { |
284 | 0 | num_loops_ = std::exchange(b.num_loops_, 0); |
285 | 0 | prev_loop_.store(b.prev_loop_.exchange(0, std::memory_order_relaxed), |
286 | 0 | std::memory_order_relaxed); |
287 | 0 | vertices_ = std::move(b.vertices_); |
288 | 0 | loop_starts_ = std::move(b.loop_starts_); |
289 | 0 | return *this; |
290 | 0 | } |
291 | | |
292 | 0 | bool EncodedS2LaxPolygonShape::Init(Decoder* decoder) { |
293 | 0 | if (decoder->avail() < 1) return false; |
294 | 0 | uint8_t version = decoder->get8(); |
295 | 0 | if (version != kCurrentEncodingVersionNumber) return false; |
296 | | |
297 | 0 | uint32_t num_loops; |
298 | 0 | if (!decoder->get_varint32(&num_loops)) return false; |
299 | | // `loop_starts_` is indexed by an integer, and contains an extra entry, |
300 | | // so we limit num_loops to INT32_MAX - 1. |
301 | 0 | if (num_loops > std::numeric_limits<int32_t>::max() - 1u) return false; |
302 | 0 | num_loops_ = num_loops; |
303 | |
|
304 | 0 | if (!vertices_.Init(decoder)) return false; |
305 | | |
306 | 0 | if (num_loops_ > 1) { |
307 | 0 | if (!loop_starts_.Init(decoder)) return false; |
308 | 0 | } |
309 | 0 | return true; |
310 | 0 | } |
311 | | |
312 | | // The encoding must be identical to S2LaxPolygonShape::Encode(). |
313 | | void EncodedS2LaxPolygonShape::Encode(Encoder* encoder, |
314 | 0 | s2coding::CodingHint) const { |
315 | 0 | encoder->Ensure(1 + Varint::kMax32); |
316 | 0 | encoder->put8(kCurrentEncodingVersionNumber); |
317 | 0 | encoder->put_varint32(num_loops_); |
318 | 0 | vertices_.Encode(encoder); |
319 | 0 | if (num_loops_ > 1) { |
320 | 0 | loop_starts_.Encode(encoder); |
321 | 0 | } |
322 | 0 | } |
323 | | |
324 | 0 | int EncodedS2LaxPolygonShape::num_vertices() const { |
325 | 0 | if (num_loops() <= 1) { |
326 | 0 | return vertices_.size(); |
327 | 0 | } else { |
328 | 0 | return loop_starts_[num_loops()]; |
329 | 0 | } |
330 | 0 | } |
331 | | |
332 | 0 | int EncodedS2LaxPolygonShape::num_loop_vertices(int i) const { |
333 | 0 | ABSL_DCHECK_LT(i, num_loops()); |
334 | 0 | if (num_loops() == 1) { |
335 | 0 | return vertices_.size(); |
336 | 0 | } else { |
337 | 0 | return loop_starts_[i + 1] - loop_starts_[i]; |
338 | 0 | } |
339 | 0 | } |
340 | | |
341 | 0 | S2Point EncodedS2LaxPolygonShape::loop_vertex(int i, int j) const { |
342 | 0 | ABSL_DCHECK_LT(i, num_loops()); |
343 | 0 | ABSL_DCHECK_LT(j, num_loop_vertices(i)); |
344 | 0 | if (num_loops() == 1) { |
345 | 0 | return vertices_[j]; |
346 | 0 | } else { |
347 | 0 | return vertices_[loop_starts_[i] + j]; |
348 | 0 | } |
349 | 0 | } |
350 | | |
351 | 0 | S2Shape::Edge EncodedS2LaxPolygonShape::edge(int e) const { |
352 | 0 | ABSL_DCHECK_LT(e, num_edges()); |
353 | 0 | size_t e1 = e + 1; |
354 | 0 | if (num_loops() == 1) { |
355 | 0 | if (e1 == vertices_.size()) { e1 = 0; } |
356 | 0 | return Edge(vertices_[e], vertices_[e1]); |
357 | 0 | } else { |
358 | | // Method names are fully specified to enable inlining. |
359 | 0 | ChainPosition pos = EncodedS2LaxPolygonShape::chain_position(e); |
360 | 0 | return EncodedS2LaxPolygonShape::chain_edge(pos.chain_id, pos.offset); |
361 | 0 | } |
362 | 0 | } |
363 | | |
364 | 0 | S2Shape::ReferencePoint EncodedS2LaxPolygonShape::GetReferencePoint() const { |
365 | 0 | return s2shapeutil::GetReferencePoint(*this); |
366 | 0 | } |
367 | | |
368 | 0 | S2Shape::Chain EncodedS2LaxPolygonShape::chain(int i) const { |
369 | 0 | ABSL_DCHECK_LT(i, num_loops()); |
370 | 0 | if (num_loops() == 1) { |
371 | 0 | return Chain(0, vertices_.size()); |
372 | 0 | } else { |
373 | 0 | int start = loop_starts_[i]; |
374 | 0 | return Chain(start, loop_starts_[i + 1] - start); |
375 | 0 | } |
376 | 0 | } |