1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
|
// Copyright 2009 Google Inc. All Rights Reserved.
// julienbasch@google.com (Julien Basch)
// Implementation of class S2EdgeIndex, a fast lookup structure for edges in S2.
//
// An object of this class contains a set S of edges called the test edges.
// For a query edge q, you want to compute a superset of all test edges that
// intersect q.
//
// The idea is roughly that of
// Each edge is covered by one or several S2 cells, stored in a multimap
// cell -> edge*.
// To perform a query, you cover the query edge with a set of cells. For
// each such cell c, you find all test edges that are in c,in an ancestor of c
// or in a child of c.
//
// This is simple, but there are two complications:
//
// 1. For containment queries, the query edge is very long (from S2::Origin()
// to the query point). A standard cell covering of q is either useless or
// too large. The covering needs to be adapted to S: if a cell contains too
// many edges from S, you subdivide it and keep only the subcells that
// intersect q. See comments for FindCandidateCrossings().
//
// 2. To decide if edge q could possibly cross edge e, we end up comparing
// both with edges that bound s2 cells. Numerical inaccuracies
// can lead to inconcistencies, e.g.: there may be an edge b at the
// boundary of two cells such that q and e are on opposite sides of b,
// yet they cross each other. This special case happens a lot if your
// test and query edges are cell boundaries themselves, and this in turn
// is a common case when regions are approximated by cell unions.
//
// We expand here on the solution to the second problem. Two components:
//
// 1. Each test edge is thickened to a rectangle before it is S2-covered.
// See the comment for GetThickenedEdgeCovering().
//
// 2. When recursing through the children of a cell c for a query edge q,
// we test q against the boundaries of c's children in a 'lenient'
// way. That is, instead of testing e.g. area(abc)*area(abd) < 0,
// we check if it is 'approximately negative'.
//
// To see how the second point is necessary, imagine that your query
// edge q is the North boundary of cell x. We recurse into the four
// children a,b,c,d of x. To do so, we check if q crosses or touches any
// of a,b,c or d boundaries. As all the situations are degenerate, it is
// possible that all crossing tests return false, thus making q suddenly
// 'disappear'. Using the lenient crossing test, we are guaranteed that q
// will intersect one of the four edges of the cross that bounds a,b,c,d.
// The same holds true if q passes through the cell center of x.
#include "s2edgeindex.h"
#include <algorithm>
using std::min;
using std::max;
using std::swap;
using std::reverse;
#include <set>
using std::set;
using std::multiset;
#include <utility>
using std::pair;
using std::make_pair;
#include "base/logging.h"
#include "s2cell.h"
#include "s2edgeutil.h"
#include "s2polyline.h"
#include "s2regioncoverer.h"
//"When we test a query edge against a cell, we don't "
//"recurse if there are only a few test edges in it. "
//"For testing, it is useful to always recurse to the end. "
//"You don't want to use this flag anywhere but in tests.";
static bool FLAGS_always_recurse_on_children = false;
void S2EdgeIndex::Reset() {
minimum_s2_level_used_ = S2CellId::kMaxLevel;
index_computed_ = false;
query_count_ = 0;
mapping_.clear();
}
void S2EdgeIndex::ComputeIndex() {
DCHECK(!index_computed_);
for (int i = 0; i < num_edges(); ++i) {
S2Point from, to;
vector<S2CellId> cover;
int level = GetCovering(*edge_from(i), *edge_to(i),
true, &cover);
minimum_s2_level_used_ = min(minimum_s2_level_used_, level);
for (vector<S2CellId>::const_iterator it = cover.begin(); it != cover.end();
++it) {
mapping_.insert(make_pair(*it, i));
}
}
index_computed_ = true;
}
bool S2EdgeIndex::IsIndexComputed() const {
return index_computed_;
}
void S2EdgeIndex::IncrementQueryCount() {
query_count_++;
}
// If we have m data edges and n query edges, then the brute force cost is
// m * n * test_cost
// where test_cost is taken to be the cost of EdgeCrosser::RobustCrossing,
// measured to be about 30ns at the time of this writing.
//
// If we compute the index, the cost becomes:
// m * cost_insert + n * cost_find(m)
//
// - cost_insert can be expected to be reasonably stable, and was measured
// at 1200ns with the BM_QuadEdgeInsertionCost benchmark.
//
// - cost_find depends on the length of the edge . For m=1000 edges,
// we got timings ranging from 1ms (edge the length of the polygon) to
// 40ms. The latter is for very long query edges, and needs to be
// optimized. We will assume for the rest of the discussion that
// cost_find is roughly 3ms.
//
// When doing one additional query, the differential cost is
// m * test_cost - cost_find(m)
// With the numbers above, it is better to use the quad tree (if we have it)
// if m >= 100.
//
// If m = 100, 30 queries will give m*n*test_cost = m*cost_insert = 100ms,
// while the marginal cost to find is 3ms. Thus, this is a reasonable
// thing to do.
void S2EdgeIndex::PredictAdditionalCalls(int n) {
if (index_computed_) return;
if (num_edges() > 100 && (query_count_ + n) > 30) {
ComputeIndex();
}
}
void S2EdgeIndex::GetEdgesInParentCells(
const vector<S2CellId>& cover,
const CellEdgeMultimap& mapping,
int minimum_s2_level_used,
vector<int>* candidate_crossings) {
// Find all parent cells of covering cells.
set<S2CellId> parent_cells;
for (vector<S2CellId>::const_iterator it = cover.begin(); it != cover.end();
++it) {
for (int parent_level = it->level() - 1;
parent_level >= minimum_s2_level_used;
--parent_level) {
if (!parent_cells.insert(it->parent(parent_level)).second) {
break; // cell is already in => parents are too.
}
}
}
// Put parent cell edge references into result.
for (set<S2CellId>::const_iterator it = parent_cells.begin(); it
!= parent_cells.end(); ++it) {
pair<CellEdgeMultimap::const_iterator,
CellEdgeMultimap::const_iterator> range =
mapping.equal_range(*it);
for (CellEdgeMultimap::const_iterator it2 = range.first;
it2 != range.second; ++it2) {
candidate_crossings->push_back(it2->second);
}
}
}
// Returns true if ab possibly crosses cd, by clipping tiny angles to
// zero.
static bool LenientCrossing(S2Point const& a, S2Point const& b,
S2Point const& c, S2Point const& d) {
DCHECK(S2::IsUnitLength(a));
DCHECK(S2::IsUnitLength(b));
DCHECK(S2::IsUnitLength(c));
// See comment for RobustCCW() in s2.h
const double kMaxDetError = 1.e-14;
double acb = a.CrossProd(c).DotProd(b);
double bda = b.CrossProd(d).DotProd(a);
if (fabs(acb) < kMaxDetError || fabs(bda) < kMaxDetError) {
return true;
}
if (acb * bda < 0) return false;
double cbd = c.CrossProd(b).DotProd(d);
double dac = d.CrossProd(a).DotProd(c);
if (fabs(cbd) < kMaxDetError || fabs(dac) < kMaxDetError) {
return true;
}
return (acb * cbd >= 0) && (acb * dac >= 0);
}
bool S2EdgeIndex::EdgeIntersectsCellBoundary(
S2Point const& a, S2Point const& b, const S2Cell& cell) {
//S2Point start_vertex = cell.GetVertex(0);
S2Point vertices[4];
for (int i = 0; i < 4; ++i) {
vertices[i] = cell.GetVertex(i);
}
for (int i = 0; i < 4; ++i) {
S2Point from_point = vertices[i];
S2Point to_point = vertices[(i+1) % 4];
if (LenientCrossing(a, b, from_point, to_point)) {
return true;
}
}
return false;
}
void S2EdgeIndex::GetEdgesInChildrenCells(
S2Point const& a, S2Point const& b,
vector<S2CellId>* cover,
const CellEdgeMultimap& mapping,
vector<int>* candidate_crossings) {
CellEdgeMultimap::const_iterator it, start, end;
int num_cells = 0;
// Put all edge references of (covering cells + descendant cells) into result.
// This relies on the natural ordering of S2CellIds.
while (!cover->empty()) {
S2CellId cell = cover->back();
cover->pop_back();
num_cells++;
start = mapping.lower_bound(cell.range_min());
end = mapping.upper_bound(cell.range_max());
int num_edges = 0;
bool rewind = FLAGS_always_recurse_on_children;
// TODO(user): Maybe distinguish between edges in current cell, that
// are going to be added anyhow, and edges in subcells, and rewind only
// those.
if (!rewind) {
for (it = start; it != end; ++it) {
candidate_crossings->push_back(it->second);
++num_edges;
if (num_edges == 16 && !cell.is_leaf()) {
rewind = true;
break;
}
}
}
// If there are too many to insert, uninsert and recurse.
if (rewind) {
for (int i = 0; i < num_edges; ++i) {
candidate_crossings->pop_back();
}
// Add cells at this level
pair<CellEdgeMultimap::const_iterator,
CellEdgeMultimap::const_iterator> eq =
mapping.equal_range(cell);
for (it = eq.first; it != eq.second; ++it) {
candidate_crossings->push_back(it->second);
}
// Recurse on the children -- hopefully some will be empty.
if (eq.first != start || eq.second != end) {
S2Cell children[4];
S2Cell c(cell);
c.Subdivide(children);
for (int i = 0; i < 4; ++i) {
// TODO(user): Do the check for the four cells at once,
// as it is enough to check the four edges between the cells. At
// this time, we are checking 16 edges, 4 times too many.
//
// Note that given the guarantee of AppendCovering, it is enough
// to check that the edge intersect with the cell boundary as it
// cannot be fully contained in a cell.
if (EdgeIntersectsCellBoundary(a, b, children[i])) {
cover->push_back(children[i].id());
}
}
}
}
}
VLOG(1) << "Num cells traversed: " << num_cells;
}
// Appends to "candidate_crossings" all edge references which may cross the
// given edge. This is done by covering the edge and then finding all
// references of edges whose coverings overlap this covering. Parent cells
// are checked level by level. Child cells are checked all at once by taking
// advantage of the natural ordering of S2CellIds.
void S2EdgeIndex::FindCandidateCrossings(
S2Point const& a, S2Point const& b,
vector<int>* candidate_crossings) const {
DCHECK(index_computed_);
vector<S2CellId> cover;
GetCovering(a, b, false, &cover);
GetEdgesInParentCells(cover, mapping_, minimum_s2_level_used_,
candidate_crossings);
// TODO(user): An important optimization for long query
// edges (Contains queries): keep a bounding cap and clip the query
// edge to the cap before starting the descent.
GetEdgesInChildrenCells(a, b, &cover, mapping_, candidate_crossings);
// Remove duplicates: This is necessary because edge references are
// inserted into the map once for each covering cell. (Testing shows
// this to be at least as fast as using a set.)
sort(candidate_crossings->begin(), candidate_crossings->end());
candidate_crossings->erase(
unique(candidate_crossings->begin(), candidate_crossings->end()),
candidate_crossings->end());
}
// Returns the smallest cell containing all four points, or Sentinel
// if they are not all on the same face.
// The points don't need to be normalized.
static S2CellId ContainingCell(S2Point const& pa, S2Point const& pb,
S2Point const& pc, S2Point const& pd) {
S2CellId a = S2CellId::FromPoint(pa);
S2CellId b = S2CellId::FromPoint(pb);
S2CellId c = S2CellId::FromPoint(pc);
S2CellId d = S2CellId::FromPoint(pd);
if (a.face() != b.face() || a.face() != c.face() || a.face() != d.face()) {
return S2CellId::Sentinel();
}
while (a != b || a != c || a != d) {
a = a.parent();
b = b.parent();
c = c.parent();
d = d.parent();
}
return a;
}
// Returns the smallest cell containing both points, or Sentinel
// if they are not all on the same face.
// The points don't need to be normalized.
static S2CellId ContainingCell(S2Point const& pa, S2Point const& pb) {
S2CellId a = S2CellId::FromPoint(pa);
S2CellId b = S2CellId::FromPoint(pb);
if (a.face() != b.face()) return S2CellId::Sentinel();
while (a != b) {
a = a.parent();
b = b.parent();
}
return a;
}
int S2EdgeIndex::GetCovering(
S2Point const& a, S2Point const& b,
bool thicken_edge,
vector<S2CellId>* edge_covering) const {
edge_covering->clear();
// Thicken the edge in all directions by roughly 1% of the edge length when
// thicken_edge is true.
static const double kThickening = 0.01;
// Selects the ideal s2 level at which to cover the edge, this will be the
// level whose S2 cells have a width roughly commensurate to the length of
// the edge. We multiply the edge length by 2*kThickening to guarantee the
// thickening is honored (it's not a big deal if we honor it when we don't
// request it) when doing the covering-by-cap trick.
const double edge_length = a.Angle(b);
const int ideal_level = S2::kMinWidth.GetMaxLevel(
edge_length * (1 + 2 * kThickening));
S2CellId containing_cell;
if (!thicken_edge) {
containing_cell = ContainingCell(a, b);
} else {
if (ideal_level == S2CellId::kMaxLevel) {
// If the edge is tiny, instabilities are more likely, so we
// want to limit the number of operations.
// We pretend we are in a cell much larger so as to trigger the
// 'needs covering' case, so we won't try to thicken the edge.
containing_cell = S2CellId(0xFFF0).parent(3);
} else {
S2Point pq = (b - a) * kThickening;
S2Point ortho = pq.CrossProd(a).Normalize() *
edge_length * kThickening;
S2Point p = a - pq;
S2Point q = b + pq;
// If p and q were antipodal, the edge wouldn't be lengthened,
// and it could even flip! This is not a problem because
// ideal_level != 0 here. The farther p and q can be is roughly
// a quarter Earth away from each other, so we remain
// Theta(kThickening).
containing_cell = ContainingCell(p - ortho, p + ortho,
q - ortho, q + ortho);
}
}
// Best case: edge is fully contained in a cell that's not too big.
if (containing_cell != S2CellId::Sentinel() &&
containing_cell.level() >= ideal_level - 2) {
edge_covering->push_back(containing_cell);
return containing_cell.level();
}
if (ideal_level == 0) {
// Edge is very long, maybe even longer than a face width, so the
// trick below doesn't work. For now, we will add the whole S2 sphere.
// TODO(user): Do something a tad smarter (and beware of the
// antipodal case).
for (S2CellId cellid = S2CellId::Begin(0); cellid != S2CellId::End(0);
cellid = cellid.next()) {
edge_covering->push_back(cellid);
}
return 0;
}
// TODO(user): Check trick below works even when vertex is at interface
// between three faces.
// Use trick as in S2PolygonBuilder::PointIndex::FindNearbyPoint:
// Cover the edge by a cap centered at the edge midpoint, then cover
// the cap by four big-enough cells around the cell vertex closest to the
// cap center.
S2Point middle = ((a + b) / 2.0).Normalize();
int actual_level = min(ideal_level, S2CellId::kMaxLevel-1);
S2CellId::FromPoint(middle).AppendVertexNeighbors(
actual_level, edge_covering);
return actual_level;
}
void S2EdgeIndex::Iterator::GetCandidates(S2Point const& a, S2Point const& b) {
edge_index_->PredictAdditionalCalls(1);
is_brute_force_ = !edge_index_->IsIndexComputed();
if (is_brute_force_) {
edge_index_->IncrementQueryCount();
current_index_ = 0;
num_edges_ = edge_index_->num_edges();
} else {
candidates_.clear();
edge_index_->FindCandidateCrossings(a, b, &candidates_);
current_index_in_candidates_ = 0;
if (!candidates_.empty()) {
current_index_ = candidates_[0];
}
}
}
|